4 पॉइंट द्वारा GN⁺ 2023-09-30 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Red Blob Games के इंटरैक्टिव डायग्राम में, जब पाठक किसी ऑब्जेक्ट को सीधे हिलाते हैं तो स्क्रीन को तुरंत प्रतिक्रिया देनी होती है, इसलिए input handling को Pointer Events पर केंद्रित करके व्यवस्थित किया गया है ताकि mouse और touch दोनों को साथ संभाला जा सके
  • पुराने mousedown/mouseup/mousemove तरीके में, जब pointer ऑब्जेक्ट से बाहर निकल जाता था या बाहर button छोड़ा जाता था, तो drag state गड़बड़ा सकती थी, इसलिए global handler जैसे पूरक उपायों की जरूरत पड़ती थी
  • touch input में event delivery का तरीका mouse से अलग होता है, और एक उंगली से drag करने पर page scroll भी हो सकता है, इसलिए टकराव कम करने के लिए touchstart में preventDefault() को call किया जाता है
  • अंतिम recipe में pointerdown, pointermove, pointerup, pointercancel और setPointerCapture() को मिलाया गया है, और left button व शुरुआती offset की जांच करके यह सुनिश्चित किया गया है कि ऑब्जेक्ट अचानक उछले नहीं
  • text·image वाले ऑब्जेक्ट, multi-touch, nested drag, <canvas>, hover और drag का combination, lostpointercapture जैसी platform-specific exceptions को वास्तविक UI में अलग से handle करना पड़ता है

Drag input handler का लक्ष्य

  • कई interactive pages में ऐसे draggable objects होते हैं जिन्हें पाठक सीधे हिला सकते हैं, और diagram को object position में बदलाव पर प्रतिक्रिया देनी होती है
  • लक्ष्य ऐसा input event handler बनाना है जो mouse और touch input दोनों पर काम करे
  • basic flow यह है कि pointerdown पर drag शुरू हो, pointermove पर position update हो, और pointerup या cancel event पर drag समाप्त हो
  • actual implementation में mouse buttons, कई उंगलियों का touch, event delivery target, और right-click context menu की वजह से यह simple model से ज्यादा जटिल हो जाता है
  • basic recipe इन elements को combine करती है
    • pointerdown में अगर event.button !== 0 हो तो return करके केवल primary button process करें
    • event coordinates को diagram coordinates में convert करें
    • शुरुआत में object position और pointer position के बीच का अंतर dx, dy store करें
    • el.setPointerCapture(event.pointerId) से pointer capture करें
    • pointermove में stored offset जोड़कर state.pos update करें
    • pointerup, pointercancel में state.dragging = null
    • touchstart में preventDefault() call करें
  • यह recipe input event handler है, और actual output processing को अलग state handler से जोड़ना होगा
  • पूरा code examples page पर है, जिसमें object movement के अलावा number को left-right drag करना, canvas painting, और restricted drag के examples भी शामिल हैं
  • test coverage में Gecko/Firefox, Blink/Chrome, WebKit/Safari के Mac, Windows, Linux, Android, iPhone, iPad शामिल हैं
  • hover-capable stylus, hybrid touch+mouse devices, और voice input को test नहीं किया गया
  • यह तरीका HTML Drag and Drop API नहीं है, जिसमें किसी element को दूसरे element के ऊपर रखा जाता है; diagrams में mouse·touch events को सीधे पढ़ना पड़ता है

केवल mouse events इस्तेमाल करने की सीमाएं

  • शुरुआती implementation में mousedown, mouseup, mousemove को सीधे draggable element पर attach किया गया था
  • ऊपर से यह काम करता हुआ दिखता है, लेकिन दो समस्याएं आती हैं
    • pointer को तेजी से हिलाने पर वह अब circle के ऊपर नहीं रहता, इसलिए events नहीं मिलते
    • circle के बाहर button छोड़ने पर dragging state में फंस सकते हैं
  • सुधार यह है कि mousedown circle पर लिया जाए, और drag के दौरान mousemovemouseup को अस्थायी रूप से document पर attach किया जाए
  • mouseup में document पर लगे mousemove, mouseup handlers हटाने से mouse drag ज्यादा stable तरीके से काम करता है
  • यह तरीका केवल mouse को handle करता है और touch events को नहीं संभालता

touch events का अंतर

  • touch input में mousedown, mouseup, mousemove की जगह touchstart, touchend, touchmove का इस्तेमाल होता है
  • touchstart के बाद touch events अपने-आप capture हो जाते हैं और सभी touchmove मूल element तक deliver होते हैं
  • इस वजह से mouse events की तरह document पर temporary global event handler attach करने की जरूरत नहीं होती
  • जब browser को touch sequence cancel करना होता है, तो वह touchcancel भेजता है
  • touch drag implementation touchstart में preventDefault() call करता है, और touchmove में event.changedTouches[0] coordinates से position update करता है
  • यह तरीका केवल touch को handle करता है और mouse events को process नहीं करता

Pointer Events से mouse और touch को integrate करना

  • mouse और touch दोनों को handle करने के लिए कई event handlers की जरूरत पड़ती थी, और 2021 से पहले इसी तरह का तरीका इस्तेमाल होता था
  • 2011~2014 में d3 projects में d3-drag का इस्तेमाल किया गया, और जिन projects में d3 नहीं था उनमें अलग mouse+touch code बनाया गया
  • Pointer Events mouse और touch handling को integrate और simplify करते हैं
    • MS IE ने 2012 में support जोड़ा
    • Chrome ने 2017 में, Firefox ने 2018 में, और Safari ने 2020 में support जोड़ा
  • browser behavior समय के साथ बदला, और 2017 में Chrome ने कुछ events को default passive mode में बदल दिया, जिससे object को drag करने की कोशिश करते समय page scroll होने की समस्या आई
  • Safari और Firefox ने भी 2018 में वही बदलाव लागू किया
  • pointer capture का इस्तेमाल करने पर mouse events में जिन document global handlers को add/remove करना पड़ता था, उनके बिना simple logic से काम हो जाता है

Pointer Capture से pointer tracking बनाए रखना

  • Pointer capture pointer के circle, diagram, या browser window से बाहर निकल जाने पर भी tracking जारी रखने देता है
  • mouse events में वही effect पाने के लिए document पर event handler attach करना पड़ता था, लेकिन Pointer Events में setPointerCapture(event.pointerId) ज्यादा simple है
  • pointer capture न हो तो तेजी से drag करने या diagram से बाहर जाकर वापस आने पर drag रुक सकता है
  • diagram के बाहर button छोड़ने या Alt+Tab से दूसरी window में जाने जैसे मामलों में भी capture होने या न होने के आधार पर drag end handling अलग होती है
  • touch devices में pointer capture default behavior है, लेकिन mouse में इसे explicitly set करना पड़ता है

touch scroll और preventDefault()

  • touch devices पर एक उंगली से drag करना default रूप से page scroll कराता है
  • अगर उसी one-finger drag से circle भी move हो, तो scroll और object drag साथ-साथ होते हैं
  • सबसे simple solution diagram पर CSS touch-action: none apply करना है, लेकिन ऐसे में diagram के किसी भी हिस्से में scroll नहीं किया जा सकता
  • desired behavior यह है कि circle drag करते समय ही scroll रोका जाए, और diagram को drag करते समय scroll allow रहे
  • comparison result इस प्रकार है
    • पूरे diagram पर touch-action: none: circle drag और diagram drag दोनों में scroll रुकता है
    • default value: दोनों में scroll को रोक नहीं पाती
    • केवल circle पर touch-action: none: उम्मीद के मुताबिक काम नहीं करता
    • touchstart में preventDefault(): circle drag के दौरान केवल scroll रोकता है और diagram scroll allow करता है
  • preventDefault() को pointerstart पर नहीं, बल्कि touchstart पर attach करें
  • यह तरीका ज्यादातर स्थितियों में काम करता है, लेकिन desktop Firefox के “mobile emulation mode” में काम नहीं किया, जबकि Android के mobile Firefox में काम करता है

ड्रैग शुरू होने का ऑफसेट सहेजना

  • ऑब्जेक्ट के किनारे को पकड़कर ड्रैग करते समय अगर ऑब्जेक्ट का केंद्र तुरंत pointer की position पर jump कर जाए, तो usability खराब होती है
  • समाधान है कि ड्रैग शुरू होते समय ऑब्जेक्ट के केंद्र और pointer position की relative position को सहेजा जाए
  • इसके बाद move करते समय मौजूदा pointer coordinates में सहेजे गए dx, dy जोड़कर ऑब्जेक्ट की position calculate की जाती है
  • इस तरीके में state.dragging को simple true/false के बजाय {dx, dy} जैसी relative position जानकारी में बदला जाता है
  • mouse में किनारे से पकड़ने पर jump का फर्क साफ दिखता है, जबकि touch में उंगली से ढक जाने की वजह से यह कम दिख सकता है
  • इससे जुड़ी छोटी detail little details पेज पर भी संक्षेप में दी गई है

context menu और mouse button

  • context menu activate करने का तरीका हर platform पर अलग होता है
    • Windows: right-click down+up, Shift+F10
    • Linux: right button down, Shift+F10
    • Mac: right button down, Ctrl+left click
    • iOS: केवल text पर long press
    • Android: कहीं भी long press
  • समस्या यह है कि कभी-कभी pointerdown दिखता है, लेकिन pointerup हमेशा नहीं दिखता
  • button असल में release हो चुका हो, फिर भी code में उसे अभी pressed state में माना जा सकता है
  • इसका उपाय है right button को ignore करना और केवल left mouse button पर ही dragging state set करना
  • Pointer Events specification के button property में button = 0 primary button को दर्शाता है, और इसका उपयोग करके middle और right button को exclude किया जाता है
  • Mac के Chrome और Safari में Ctrl+click left button pointerdown की तरह आ सकता है, इसलिए event.ctrlKey भी check करना चाहिए
  • विकल्प इस प्रकार हैं
    • contextmenu में preventDefault() से menu को रोका जा सकता है, लेकिन यह default middle click या drag behavior से conflict कर सकता है
    • केवल left button से drag करने पर right-click से जुड़े ज्यादातर differences से बचा जा सकता है
    • Mac Ctrl+click से भी बचना हो तो ctrlKey को भी exclude करना चाहिए
  • button changes pointerdown या pointerup के रूप में deliver न हों, तब भी pointermove के button state से कुछ हद तक check किया जा सकता है

text और image वाले ऑब्जेक्ट

  • अगर draggable element के अंदर text या image हो, तो अतिरिक्त handling की जरूरत होती है
  • circle को drag करते समय अंदर का text select हो सकता है
  • समाधान है CSS user-select: none apply करना
    • हमेशा apply करने पर अंदर का text कभी select नहीं होगा
    • केवल drag के दौरान apply करने पर सामान्य समय में text selection संभव रहेगा
  • 2023 की शुरुआत तक Safari अभी unprefixed user-select support नहीं करता था, इसलिए webkitUserSelect भी साथ में set करना चाहिए
  • Windows, Linux, Mac text और image के applications के बीच drag and drop को support करते हैं, और यह object drag से conflict करता है
  • dragstart में preventDefault() call करने से selected text या image drag के बजाय object drag को priority दी जा सकती है
  • browser और operating system के अनुसार behavior थोड़ा-थोड़ा अलग होता है

multiple pointers और simultaneous drag

  • कई fingers या कई mouse से कई objects को एक साथ drag किया जा सकता है या नहीं, यह edge case है
  • touch में दिया गया code कई objects को साथ-साथ drag करने के लिए पहले से काम करता है
  • दो fingers से उसी object को drag करने के मामले में अलग handling की जरूरत होती है
  • समाधान है कि pointerdown में event.pointerId को state.dragging या state में save किया जाए, और pointermove में अगर वही pointerId नहीं है तो ignore किया जाए
  • यह implementation example article में पूरी तरह apply नहीं किया गया है, और इसे canvas dragging test में देखा जा सकता है
  • mouse के मामले में Pointer Events specification बताता है कि सामान्य operating systems और user agents में आम तौर पर multiple mouse inputs की concept नहीं होती
  • trackpad और external mouse साथ होने पर भी आम तौर पर उन्हें एक ही mouse pointer माना जाता है, इसलिए अलग-अलग mouse से अलग objects drag करने का तरीका नहीं माना जाता

कई mouse buttons साथ में दबाने पर

  • कई mouse buttons एक साथ दबाने पर Pointer Events और Mouse Events का behavior अलग होता है
  • Mouse Events हर button press पर mousedown, और हर button release पर mouseup भेजते हैं
  • Pointer Events specification के अनुसार पहले दबाए गए button पर pointerdown, और आखिरी release किए गए button पर pointerup fire होता है
  • इस वजह से pointerdown और pointerup अलग-अलग button states में हो सकते हैं
  • उदाहरण के लिए left button दबाकर, right button दबाने के बाद, left button छोड़ने पर object लगातार dragging state में रह सकता है
  • समाधान है कि pointermove में event.buttons & 1 से left button state check की जाए, और अगर वह अब pressed नहीं है तो end(event) call किया जाए
  • pointer capture सभी buttons छोड़े जाने तक जारी रह सकता है, और explicitly capture release न करने तक बना रह सकता है
  • यह implementation इस समस्या और कई edge cases को पूरी तरह handle नहीं करता

nested drag elements

  • अगर draggable element के अंदर एक और draggable element हो, तो inner और outer दोनों elements drag को handle करते हैं
  • समाधान है कि inner drag element में event.stopPropagation() call करके event को outer drag element तक ऊपर जाने से रोका जाए
  • pointerdown और pointermove दोनों में propagation रोकने का तरीका इस्तेमाल होता है
  • demo article में नहीं है, लेकिन Vue playground example में red drag element के yellow drag element का child होने वाला case देखा जा सकता है

Canvas में Drag

  • आम तौर पर SVG का इस्तेमाल किया जाता है, लेकिन <canvas> में drag किए जा सकने वाले element पर सीधे event handler या mouse pointer का आकार सेट नहीं किया जा सकता
  • 2D Canvas या WebGL में event handler को <canvas> पर लगाना पड़ता है, और खुद तय करना पड़ता है कि coordinates किसी draggable object के ऊपर हैं या नहीं -处理 करने का तरीका इस प्रकार है
    • pointerdown, touchstart, dragstart: अगर draggable object के ऊपर नहीं है, तो तुरंत return करें
    • pointermove: cursor को इस आधार पर सेट करें कि वह draggable object के ऊपर है या नहीं
  • Demo examples page पर है

Mouse Hover और Touch Drag का संयोजन

  • कुछ diagrams mouse पर button दबाए बिना hover से react करते हैं, और touch devices में hover नहीं होता, इसलिए उन्हें drag से react करना चाहिए
  • Basic recipe mouse और touch दोनों में drag की उम्मीद करती है
  • Mouse hover और touch drag को साथ इस्तेमाल करने के लिए pointermove में if (!state.dragging) condition हटाएँ
  • Responsive Design page पर mouse से row पर hover करने पर layout बदलता है, और touch में drag करने पर layout बदलता है
  • Hexagons Guide में भी कई diagrams mouse hover और touch drag को साथ इस्तेमाल करते हैं
  • Touch में item A से B तक drag करते समय अगर capture बना रहता है, तो move event A को deliver होता है, इसलिए B को highlight करने के लिए capture release करना पड़ सकता है

Toggle Paint और Drag State का विस्तार

  • Rounded Cell Painter में pointerdown शुरुआती paint color को capture करता है
  • इसके बाद pointerup तक सभी pointermove वही paint color इस्तेमाल करते हैं
  • इसके लिए state.dragging में शुरुआती x, y के साथ-साथ शुरुआती paint color भी शामिल होना चाहिए
  • Demo examples page पर है

lostpointercapture Edge Case

  • lostpointercapture का इस्तेमाल यह detect करने के लिए किया जा सकता है कि pointer capture खो गया है या नहीं
  • यह किन situations में होता है और क्या करना चाहिए, यह अभी पूरी तरह व्यवस्थित नहीं है
  • Comments में चर्चा किया गया scenario इस प्रकार है
    • Left button से circle drag करना शुरू करें
    • Left button दबाए रखते हुए drag area के बाहर जाएँ
    • Right button दबाएँ और फिर left button छोड़ें
    • Drag area के बाहर कहीं भी click करें
  • standard कहता है कि pointer capture release होने के बाद user agent को lostpointercapture event अनिवार्य रूप से fire करना चाहिए
  • वास्तविक browser behavior काफी अलग-अलग है; कुछ systems में lostpointercapture fire होता है, लेकिन कुछ में यह तब तक fire नहीं होता जब तक कहीं और click न किया जाए

1 टिप्पणियां

 
GN⁺ 2023-09-30
Hacker News की टिप्पणियां
  • यह लेख drag के बारे में है, और Amit ने जिन pitfalls की बात की है उनमें से लगभग सभी से मैं गुजरा हूं और उसी समाधान तक पहुंचा हूं। शानदार लेख है
    यूज़र के नजरिए से drag करके क्रम बदलना बहुत स्वाभाविक लगता है, लेकिन अलग-अलग आकार के items, items के बीच placeholder बनाना, edge detection, और box-fitting algorithms की rabbit hole तक पहुंचते-पहुंचते इसे खुद implement करना काफी मुश्किल श्रेणी में आ जाता है

    • ऐसे मामलों में मुझे एक काफी सामान्य trick पसंद है: जब यूज़र drag शुरू करे, तो सभी संभावित drop targets, या अभी दिखाई दे रहे targets के layout को calculate करें, और हर layout में drag हो रहे object की अंतिम position record करें
      mouse के हर move पर उन positions में से वर्तमान drag object position के सबसे करीब वाली चुनें, और selected layout render कर दें। complex layouts या reflow में भी यह अच्छा feel देता है
    • अपने index card app Card Buddy में मुझे यह समस्या हल करनी पड़ी थी। [1] यह एक मजेदार challenge था, और बाद में मुझे एक बेहतर तरीका भी मिला
      आखिर में मैंने ऐसा किया कि card उठाते ही layout ऐसे calculate किया जैसे वह card board से delete हो गया हो, और इससे काम आसान हो गया। बस mouse-over वाली position पर मौजूद चीजों को धकेलना था
      फिर भी exception handling बहुत ज्यादा थी, खासकर तब जब कोई नया card edit करना शुरू होता था जो अभी data model में “commit” नहीं हुआ था। मौजूदा cards को धकेलकर ghost card की जगह बनाने का option भी जोड़ना पड़ा
      यह मान लेना मददगार है कि बहुत सारे exceptions manually handle करने होंगे। अगर आप यह सोचकर approach करेंगे कि कोई ज्यादा general और homogeneous solution होगा, तो design में गोल-गोल घूमते रहेंगे। कभी न कभी अलग-अलग edge cases को blog post में整理 करना चाहिए
      मुझे एक बेहतर layout तरीका भी मिला, जो अनावश्यक calculations कम करता है और layout engine को ज्यादा flexible और user-friendly बनाता है। data model representation कैसे चुना जाता है, यह solution में कितना बड़ा फर्क डालता है, यह हैरान करने वाला है
      [1] https://www.ussherpress.com/cardbuddy/
    • अगर इसमें nesting तक support करने वाला drag reordering हो, तो यह और भी कठिन हो जाता है
    • मेरी भी यही राय है। idea सरल है, लेकिन implementation बेरहम हो सकती है। drag selection को भी मैंने कई सालों तक बहुत obvious मान लिया था
  • लगता है लेख में cancel handling को cover नहीं किया गया है। उदाहरण के लिए Windows में Escape दबाने पर drag cancel करने की convention है
    कभी-कभी आप यह भी चाहते हैं कि defined area के बाहर mouse छोड़ने पर drag cancel हो जाए। cancel यूज़र को तेज undo, या “अरे, असल में मैं drag करना नहीं चाहता था” जैसा escape route देता है
    इसलिए अलग undo feature न भी हो, तब भी drag शुरू होने के समय की original state save रखनी चाहिए और cancel होने पर restore करनी चाहिए
    area के बाहर drag cancel करने के मामले में cancel解除 भी होता है। जब pointer area के बाहर हो, तो visually दिखाएं कि original state पर वापस लौट गया है; और pointer फिर से area के अंदर आए तो drag resume हो जाए। actual cancel mouse छोड़ने पर होता है, लेकिन drag के दौरान ही पहले से visual संकेत दे दिया जाता है कि cancel होने वाला है

    • Windows में अगर आप किसी चीज को बहुत दूर drag करते हैं, तो drag हो रहा object और pointer अचानक गायब हो जाते हैं, और बिना warning के original position पर वापस उछल जाते हैं
      यकीन नहीं होता कि Microsoft अब भी इसे अच्छा UI design मानता है। यह यूज़र को पूरी तरह confuse कर देता है; कई बार लोग समझ नहीं पाते कि क्या हो रहा है और left mouse button छोड़ने से डरते हैं, या yo-yo की तरह बार-बार वही करते दिखते हैं
      सहमत हूं कि cancel एक बड़ा मुद्दा है जिसे अक्सर नजरअंदाज किया जाता है, और यह simple नहीं है। आम यूज़र को यह भी पता नहीं होता कि ESC मदद कर सकता है
    • जानना चाहूंगा कि दूसरे platforms पर ESC के अलावा कोई अलग cancel key handle करनी पड़ती है या नहीं
  • Ardour [0] के GUI में तरह-तरह के objects drag करने का काम 20 साल करते हुए मैंने कुछ अतिरिक्त details सीखी हैं: हर button press और release event को बिना movement वाले drag event की तरह treat करना चाहिए
    यानी press हमेशा drag शुरू करता है और release पर खत्म होता है; release handle करने वाला code no-movement condition को special-case करता है
    [0] https://ardour.org/

    • वह तरीका भी काम करेगा, लेकिन मैंने ऐसा नहीं किया, और basics ठीक से पकड़ लेने के बाद कोई समस्या नहीं आई
      मेरे पास सामान्य mousedown और mouseup handlers हैं, और यह तय करने के लिए कि यह click है, drag की शुरुआत है या कोई दूसरी action, मैं करीब 150ms का configurable timeout इस्तेमाल करता हूं। mouseup आए, या mouse screen से बाहर चला गया हो और कोई drag चल रहा हो, तो stopdrag करता हूं। मेरे लिए drag कई special cases में से बस एक है
    • जानना चाहूंगा कि ऐसा करने की वजह क्या है
  • बहुत पहले Minneapolis में NSF द्वारा supported एक geometry center था। computational group theory की conference हुई थी, और आसपास के fields से mascot जैसे कुछ लोगों को invite किया गया था
    मुझे इसलिए invite मिला क्योंकि मैंने algebraic geometry system लिखा था, और -20°F की ठंड में बहुत सुबह उठकर उस दिन इस्तेमाल करने के लिए Silicon Graphics workstation हासिल किया, फिर group generators और relations को बेहतर समझने के लिए एक game code किया
    उस game में drag शामिल था। नतीजा यह हुआ कि रात 2 बजे तक चली conversations बहुत अच्छी रहीं। मेरा विचार था कि drag को real world के physics laws follow करने की जरूरत नहीं है, और drag को बहुत strong LSD tab जैसा feel होना चाहिए। हर कोई इसमें डूब गया और अपने-अपने ideas लेकर आया

  • यह लेख वेब ब्राउज़र में आने वाले कई edge cases को अलग रख दें तो अपेक्षाकृत बुनियादी drag को implement करता है। मैं जानना चाहूँगा कि क्या ऐसे drag resources हैं जिनमें constraints हों, जैसे guidelines पर snap करना, boundaries या दूसरे objects से collision रोकना, या drag action के response में size या position बदलने वाले animated drop targets
    पहले मैं एक custom Pomodoro timer UI बनाना चाहता था, जिसमें circular clock को अलग-अलग लंबाई के wedge pieces में बाँटकर focus/rest intervals define किए जाते। उन wedge pieces की drag-based reordering implement करने में ज़्यादा progress नहीं हुई

    • इस code approach में drag movement किसी state को update करता है। State set करते समय constraints apply किए जा सकते हैं, और वही state फिर display को drive करता है
      मैं constraint system को event handling system से अलग रखना चाहता था। jquery-ui जैसी libraries दोनों को बाँध देती हैं, जिससे event handling system को सभी possible constraints के बारे में पता होना पड़ता है। jquery-ui bounding box, axis, square grid, और DOM element पर snap support करता है। लेकिन अगर आप hex grid, log-scale grid, या circular boundary पर snap करना चाहें, तो वह support नहीं है
      Code में state.pos = … दिखता है, वही जगह है जहाँ state set किया जाता है। Constraints के लिए pos को setter के पीछे छिपा दें। तब drag event handling code को यह जाने बिना कि कौन-से constraints चाहिए, setter constraints apply कर सकता है
      मुझे page update करके constraint example दिखाना चाहिए। Code recipe में इस पहलू का ज़िक्र करना मैं पूरी तरह भूल गया
      पुराना constraint example https://www.redblobgames.com/articles/curved-paths/making-of... पर है, और collision avoidance example https://redblobgames.github.io/circular-obstacle-pathfinding... पर है। हालांकि drag-based reordering या animated drop targets अभी तक try नहीं किए
  • इस site का मैं भी आभारी हूँ। Uber में geo analysis के लिए hexagon system design करते समय https://www.redblobgames.com/grids/hexagons/ का बहुत reference लिया था

  • Proper interactions handle करते समय आने वाले pitfalls और सावधानियों को यह लेख अच्छी तरह summarize करता है
    ज़्यादा “ready-to-use” तरफ़, मैंने कई projects में interactjs को काफी लंबे समय तक use किया है

  • सभी का धन्यवाद। आज HN पर इसे देखकर हैरानी हुई, और मैं questions का जवाब दे सकता हूँ

    • क्या आपने HTML built-in draggable="true" attribute और drag events देखे हैं?
      यह भी जानना चाहूँगा कि built-in functionality इस code की तुलना में कहाँ कम पड़ती है
      https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement...
      https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement...
      https://developer.mozilla.org/en-US/docs/Web/HTML/Global_att...
    • JavaScript drag पर शानदार discussion था और जानकारी वाकई उपयोगी थी
      एक quick question है। dragstart जैसे built-in DOM events से drag movement को एक axis तक limit कैसे करें? मैंने dragstart/dragenter/dragover/drop events से drag and drop functionality implement की है, लेकिन movement को x-axis तक limit करने का quick तरीका नहीं मिला। याद है कि JQuery का drag and drop API इसे support करता था। मैं केवल native DOM events/API use करने की कोशिश कर रहा हूँ, इसलिए इस बारे में कोई information या pointer मिले तो अच्छा होगा
    • पहले मैं हमेशा browser UI code लिखता था, लेकिन कुछ साल का break रहा। किसी को pointer events use करते पहली बार सुन रहा हूँ
      यह देखते हुए कि Safari ने 2020 में support add किया था, क्या आपको चिंता नहीं होती कि browsers इसे support न करें? macOS Mojave users भी शायद Safari 13 या 14 use कर रहे होंगे, इसलिए उम्मीद है Safari 12 practically अब use में नहीं है, लेकिन general web users के लिए production app में drag जैसी simple functionality न चले तो यह काफी बुरा होगा
      Drag के दौरान document पर event handler add करना पुरानी practice है, और browsers use cases को सरल बनाने के लिए नई features add करते रहते हैं, लेकिन हर feature के अपने exceptions और pitfalls होते हैं। लेख में भी कहा गया है कि इसे अभी पूरी तरह cover नहीं किया गया। Final result में भी pointer events और touch events का combination बचा हुआ है
      मैं सोच रहा हूँ कि code कम होने के अर्थ में “simplicity” क्या इतने लायक है कि उसके बदले extra exceptions, कम browser support, और developers को pointer events की उन details और browser differences को समझने की cost उठानी पड़े, जो mouse events की तुलना में शायद कम understood और कम documented हैं
  • एक चीज़ छूट गई है: accessibility. कीबोर्ड ऑपरेशन से drag को कैसे संभव बनाना चाहिए?

    • यहाँ यह सवाल थोड़ा विषय से हटकर लगता है. यह कुछ वैसा ही है जैसे किसी drawing program में पूछा जाए कि keyboard user canvas को pan करने के लिए click करके hold कैसे करे. ऐसा किया ही नहीं जाता. mouse से panning करना या object को drag करना सिर्फ एक interface है; असली काम canvas panning है, और उसे हासिल करने के कई तरीके हो सकते हैं
      मेरे उदाहरण में accessibility का जवाब यह है कि उस काम को पूरा करने के लिए अतिरिक्त keyboard operation दिया जाए. बेहतर हो तो ऐसा तरीका जिसमें key को लगातार दबाए रखना न पड़े. उदाहरण के लिए, shortcut से panning mode में जाएँ और arrow keys से viewport को canvas के इधर-उधर move करें, तो काम हो जाएगा
      draggable object के मामले में काम order बदलना है. इसे accessible बनाने के लिए order बदलने का वैकल्पिक तरीका देना काफी है. उदाहरण के लिए, TAB से objects के बीच focus घुमाएँ, key से focused object को select करें, और फिर arrow keys से drag preview को उस दिशा में निकटतम valid position तक ले जाएँ
    • कुल मिलाकर जवाब “ऐसा नहीं करते” के करीब है. UI को pointer या touch device इस्तेमाल न कर सकने वाले लोगों के लिए page से interact करने हेतु drag का alternative देना चाहिए
      यानी drag implementation के अंदर keyboard support को जबरन न ठूँसें; drag feature से अलग, अर्थपूर्ण keyboard functionality जोड़नी चाहिए
    • वह movable object बन जाएगा, और लगता है इस implementation के आधार पर उसे आसानी से बनाया जा सकता है
    • TAB / Space / Enter से item select करें, और Ctrl + arrow keys से “drag किए जा रहे” element को move करें. जाहिर है, एक key press में केवल एक block ही move किया जा सकता है
  • बढ़िया. इस हफ्ते की शुरुआत में मैं एक Vue app में SVG elements को drag करना implement कर रहा था, और लेखक ने जिन लगभग सभी बातों को समझाया है, उन्हें मुझे उसी क्रम में खुद discover करना पड़ा; अंत में component भी लगभग वैसा ही बना
    हालांकि मैंने functional component के बजाय इसे useDragging नाम की composable utility के रूप में लिखा

    • शानदार. मैंने directive और slots वाले component आज़माए हैं, लेकिन composable अभी नहीं किया. क्योंकि मुझे पक्का नहीं था कि उस तरीके में event handlers कैसे set किए जाएँ. composable का मेरा experience ज़्यादा नहीं है