4 poin oleh GN⁺ 2023-09-30 | 1 komentar | Bagikan ke WhatsApp
  • Diagram interaktif Red Blob Games mengharuskan layar merespons seketika saat pembaca menggerakkan objek secara langsung, sehingga penanganan input disusun berpusat pada Pointer Events agar mouse dan sentuhan bisa ditangani bersama
  • Pendekatan lama mousedown/mouseup/mousemove dapat membuat status drag menjadi kacau saat pointer keluar dari objek atau tombol dilepas di luar area, sehingga perlu pelengkap seperti handler global
  • Input sentuh memiliki cara pengiriman event yang berbeda dari mouse, dan drag satu jari juga memicu scroll halaman, jadi preventDefault() dipanggil pada touchstart untuk mengurangi bentrokan
  • Resep akhirnya menggabungkan pointerdown, pointermove, pointerup, pointercancel, dan setPointerCapture(), serta memeriksa tombol kiri dan offset awal agar objek tidak tiba-tiba melompat
  • Objek yang berisi teks·gambar, multi-touch, drag bertingkat, <canvas>, kombinasi hover dan drag, serta pengecualian spesifik platform seperti lostpointercapture memerlukan penanganan terpisah di UI nyata

Tujuan handler input drag

  • Banyak halaman interaktif memiliki objek yang dapat diseret langsung oleh pembaca, dan diagram harus merespons perubahan posisi objek
  • Tujuannya adalah membuat handler event input yang bekerja baik untuk mouse maupun input sentuh
  • Alur dasarnya adalah memulai drag di pointerdown, memperbarui posisi di pointermove, lalu mengakhiri drag di pointerup atau event pembatalan
  • Dalam implementasi nyata, hal ini menjadi lebih rumit daripada model sederhana karena tombol mouse, sentuhan banyak jari, target pengiriman event, dan menu konteks klik kanan
  • Resep dasarnya menggabungkan elemen-elemen berikut
    • Di pointerdown, kembalikan jika event.button !== 0 agar hanya menangani tombol utama
    • Ubah koordinat event menjadi koordinat diagram
    • Simpan dx, dy, yaitu selisih antara posisi objek dan posisi pointer saat mulai
    • Lakukan pointer capture dengan el.setPointerCapture(event.pointerId)
    • Di pointermove, perbarui state.pos dengan menambahkan offset yang disimpan
    • Di pointerup, pointercancel, set state.dragging = null
    • Panggil preventDefault() di touchstart
  • Resep ini adalah handler event input; pemrosesan output sebenarnya harus dihubungkan ke handler status terpisah
  • Kode lengkap tersedia di examples page, termasuk contoh drag kiri-kanan pada angka alih-alih memindahkan objek, melukis di kanvas, dan drag terbatas
  • Cakupan pengujian meliputi Mac, Windows, Linux, Android, iPhone, dan iPad pada Gecko/Firefox, Blink/Chrome, dan WebKit/Safari
  • Stylus yang mendukung hover, perangkat hibrida touch+mouse, dan input suara belum diuji
  • Pendekatan ini bukan HTML Drag and Drop API untuk meletakkan elemen di atas elemen lain; pada diagram, event mouse·sentuh harus dibaca langsung

Batasan saat hanya memakai event mouse

  • Implementasi awal menggunakan mousedown, mouseup, dan mousemove yang dipasang langsung pada elemen drag
  • Sekilas tampak berfungsi, tetapi muncul dua masalah
    • Jika pointer digerakkan cepat, pointer tidak lagi berada di atas lingkaran sehingga event tidak diterima
    • Jika tombol dilepas di luar lingkaran, status dragging bisa terjebak
  • Perbaikannya adalah menerima mousedown di lingkaran, lalu selama drag berlangsung memasang mousemove dan mouseup sementara pada document
  • Dengan menghapus handler mousemove dan mouseup yang dipasang pada document saat mouseup, drag mouse dapat bekerja lebih stabil
  • Pendekatan ini hanya menangani mouse dan tidak menangani event sentuh

Perbedaan event sentuh

  • Input sentuh menggunakan touchstart, touchend, dan touchmove alih-alih mousedown, mouseup, dan mousemove
  • Setelah touchstart, event sentuh otomatis ditangkap sehingga semua touchmove dikirim ke elemen asal
  • Karena sifat ini, tidak perlu memasang sementara handler event global pada document seperti pada event mouse
  • Saat browser perlu membatalkan rangkaian sentuhan, browser mengirim touchcancel
  • Implementasi drag sentuh memanggil preventDefault() di touchstart, lalu memperbarui posisi di touchmove dengan koordinat event.changedTouches[0]
  • Pendekatan ini hanya menangani sentuhan dan tidak menangani event mouse

Menyatukan mouse dan sentuhan dengan Pointer Events

  • Untuk menangani mouse dan sentuhan sekaligus, dulu dibutuhkan banyak handler event, dan sebelum 2021 pendekatan seperti itu memang digunakan
  • Pada 2011~2014, proyek d3 menggunakan d3-drag, dan pada proyek yang tidak memakai d3 dibuat kode mouse+touch terpisah
  • Pointer Events menyatukan dan menyederhanakan penanganan mouse dan sentuhan
    • MS IE menambahkan dukungan pada 2012
    • Chrome menambahkan dukungan pada 2017, Firefox pada 2018, dan Safari pada 2020
  • Perilaku browser berubah seiring waktu, dan pada 2017 Chrome mengubah sebagian event ke passive mode secara default, sehingga saat mencoba menyeret objek halaman ikut melakukan scroll
  • Safari dan Firefox juga menerapkan perubahan yang sama pada 2018
  • Dengan pointer capture, logika dapat dibuat lebih sederhana tanpa perlu menambah dan menghapus handler global document seperti pada event mouse

Menjaga pelacakan pointer dengan Pointer Capture

  • Pointer capture memungkinkan pelacakan terus berjalan meski pointer keluar dari lingkaran, diagram, atau jendela browser
  • Pada event mouse, efek yang sama mengharuskan pemasangan handler event pada document, tetapi di Pointer Events setPointerCapture(event.pointerId) jauh lebih sederhana
  • Tanpa pointer capture, drag bisa berhenti saat diseret dengan cepat atau saat keluar dari diagram lalu masuk lagi
  • Jika tombol dilepas di luar diagram atau pengguna berpindah ke jendela lain dengan Alt+Tab, penanganan akhir drag juga berbeda tergantung ada tidaknya capture
  • Pada perangkat sentuh, pointer capture adalah perilaku bawaan, tetapi pada mouse harus diatur secara eksplisit

Scroll sentuh dan preventDefault()

  • Pada perangkat sentuh, drag satu jari secara default akan memicu scroll halaman
  • Jika drag satu jari yang sama juga menggerakkan lingkaran, scroll dan drag objek akan terjadi bersamaan
  • Solusi paling sederhana adalah menerapkan CSS touch-action: none pada diagram, tetapi dalam kasus ini pengguna tidak bisa melakukan scroll di bagian mana pun pada diagram
  • Perilaku yang diinginkan adalah mencegah scroll hanya saat menyeret lingkaran, dan tetap mengizinkan scroll saat menyeret diagram itu sendiri
  • Hasil perbandingannya sebagai berikut
    • touch-action: none pada seluruh diagram: drag lingkaran dan drag diagram sama-sama menghentikan scroll
    • Nilai default: keduanya tidak dapat mencegah scroll
    • touch-action: none hanya pada lingkaran: tidak bekerja seperti yang diharapkan
    • preventDefault() di touchstart: hanya mencegah scroll saat drag lingkaran, sambil tetap mengizinkan scroll diagram
  • preventDefault() dipasang pada touchstart, bukan pointerstart
  • Pendekatan ini bekerja dalam sebagian besar situasi, tetapi tidak bekerja pada “mobile emulation mode” di Firefox desktop, sementara pada Firefox mobile di Android pendekatan ini berfungsi

Menyimpan offset saat mulai drag

  • Saat menyeret dengan memegang tepi objek, pengalaman penggunaan akan buruk jika pusat objek langsung melompat ke posisi pointer
  • Solusinya adalah menyimpan posisi relatif antara pusat objek dan posisi pointer saat drag dimulai
  • Setelah itu, saat bergerak, posisi objek dihitung dengan menambahkan dx, dy yang disimpan ke koordinat pointer saat ini
  • Cara ini mengubah state.dragging dari sekadar true/false menjadi informasi posisi relatif seperti {dx, dy}
  • Pada mouse, perbedaan lompatan terlihat jelas saat memegang dari tepi, sedangkan pada sentuhan mungkin kurang terlihat karena tertutup jari
  • Detail kecil terkait juga dirangkum di halaman little details

Menu konteks dan tombol mouse

  • Cara mengaktifkan menu konteks berbeda di setiap platform
    • Windows: klik kanan down+up, Shift+F10
    • Linux: tombol kanan down, Shift+F10
    • Mac: tombol kanan down, Ctrl+klik kiri
    • iOS: long press hanya pada teks
    • Android: long press di mana saja
  • Masalahnya, pointerdown terlihat tetapi pointerup tidak selalu terlihat dalam beberapa kasus
  • Tombol sebenarnya mungkin sudah dilepas, tetapi kode masih bisa menganggapnya dalam keadaan ditekan
  • Cara menanganinya adalah mengabaikan tombol kanan dan hanya menetapkan status dragging pada tombol mouse kiri
  • Pada properti button di spesifikasi Pointer Events, button = 0 berarti primary button, dan ini dipakai untuk mengecualikan tombol tengah dan kanan
  • Di Chrome dan Safari pada Mac, Ctrl+klik bisa masuk seperti pointerdown tombol kiri sehingga event.ctrlKey juga perlu diperiksa
  • Pilihannya adalah sebagai berikut
    • Di contextmenu, menu bisa diblok dengan preventDefault(), tetapi ini dapat berbenturan dengan perilaku default klik tengah atau drag
    • Jika drag hanya dilakukan dengan tombol kiri, sebagian besar perbedaan klik kanan bisa dihindari
    • Jika ingin menghindari juga Ctrl+klik di Mac, maka ctrlKey juga harus dikecualikan
  • Meskipun perubahan tombol tidak dikirim melalui pointerdown atau pointerup, sebagian statusnya bisa diperiksa lewat status tombol pada pointermove

Objek yang berisi teks dan gambar

  • Jika ada teks atau gambar di dalam elemen yang bisa di-drag, perlu penanganan tambahan
  • Saat menyeret lingkaran, teks di dalamnya bisa ikut terseleksi
  • Solusinya adalah menerapkan CSS user-select: none
    • Jika selalu diterapkan, teks di dalamnya tidak akan pernah bisa dipilih
    • Jika hanya diterapkan saat drag berlangsung, pemilihan teks tetap bisa dilakukan dalam penggunaan biasa
  • Per awal 2023, Safari masih belum mendukung user-select tanpa prefiks sehingga webkitUserSelect juga perlu diatur
  • Windows, Linux, dan Mac mendukung drag and drop antaraplikasi untuk teks dan gambar, dan ini bertabrakan dengan drag objek
  • Dengan memanggil preventDefault() pada dragstart, drag objek bisa diprioritaskan dibanding drag teks atau gambar yang dipilih
  • Perilakunya sedikit berbeda tergantung browser dan sistem operasi

Banyak pointer dan drag simultan

  • Apakah beberapa objek bisa di-drag secara bersamaan dengan banyak jari atau banyak mouse adalah edge case
  • Pada touch, kode yang ditunjukkan sudah bekerja untuk menyeret beberapa objek sekaligus
  • Jika dua jari menyeret objek yang sama, diperlukan penanganan terpisah
  • Solusinya adalah menyimpan event.pointerId ke state.dragging atau state saat pointerdown, lalu mengabaikannya di pointermove jika pointerId-nya berbeda
  • Contoh implementasi ini belum diterapkan sepenuhnya di isi utama, tetapi bisa dilihat di canvas dragging test
  • Untuk mouse, spesifikasi Pointer Events menyatakan bahwa sistem operasi umum dan user agent biasanya tidak memiliki konsep banyak input mouse
  • Meski ada trackpad dan mouse eksternal sekaligus, biasanya tetap diperlakukan sebagai satu pointer mouse, sehingga dinilai tidak ada cara untuk menyeret objek berbeda dengan mouse yang berbeda

Saat beberapa tombol mouse ditekan bersamaan

  • Jika beberapa tombol mouse ditekan sekaligus, perilaku Pointer Events dan Mouse Events berbeda
  • Mouse Events mengirim mousedown untuk setiap penekanan tombol, dan mouseup untuk setiap pelepasan tombol
  • Menurut spesifikasi Pointer Events, pointerdown terjadi pada tombol pertama yang ditekan, dan pointerup terjadi pada tombol terakhir yang dilepas
  • Karena itu, pointerdown dan pointerup bisa terjadi pada status tombol yang berbeda
  • Misalnya, jika tombol kiri ditekan, lalu tombol kanan ditekan, lalu tombol kiri dilepas, objek bisa tetap berada dalam status drag
  • Solusinya adalah memeriksa status tombol kiri dengan event.buttons & 1 di pointermove, lalu memanggil end(event) jika tombol itu tidak lagi ditekan
  • Pointer capture bisa terus berlangsung sampai semua tombol dilepas, dan dapat tetap berlanjut kecuali capture dilepas secara eksplisit
  • Implementasi ini tidak menangani seluruh masalah ini dan semua edge case lain

Elemen drag bertingkat

  • Jika ada elemen yang bisa di-drag di dalam elemen lain yang juga bisa di-drag, elemen dalam dan luar sama-sama akan menangani drag
  • Solusinya adalah memanggil event.stopPropagation() pada elemen drag bagian dalam agar event tidak naik ke elemen drag luar
  • Cara yang dipakai adalah menghentikan propagasi pada pointerdown dan pointermove
  • Demonya tidak ada di isi utama, tetapi pada contoh Vue playground, Anda bisa melihat kasus elemen drag merah sebagai anak dari elemen drag kuning

Drag di Canvas

  • Biasanya menggunakan SVG, tetapi di <canvas> Anda tidak bisa menetapkan event handler atau bentuk pointer mouse pada elemen yang dapat diseret itu sendiri
  • Pada 2D Canvas atau WebGL, event handler harus dipasang ke <canvas>, lalu Anda harus memeriksa sendiri apakah koordinat berada di atas objek yang dapat diseret
  • Metode penanganannya sebagai berikut
    • pointerdown, touchstart, dragstart: jika tidak berada di atas objek yang dapat diseret, segera kembalikan
    • pointermove: atur kursor berdasarkan apakah pointer berada di atas objek yang dapat diseret
  • Demonya ada di examples page

Kombinasi Hover mouse dan Drag sentuh

  • Beberapa diagram merespons hover tanpa menekan tombol pada mouse, dan pada perangkat sentuh harus merespons dengan drag karena tidak ada hover
  • Resep dasarnya mengasumsikan drag untuk mouse maupun sentuh
  • Untuk menggunakan hover mouse dan drag sentuh bersama, hapus kondisi if (!state.dragging) di pointermove
  • Di Responsive Design page, layout berubah saat baris di-hover dengan mouse, dan pada sentuh layout berubah dengan drag
  • Di Hexagons Guide juga banyak diagram yang menggunakan hover mouse dan drag sentuh secara bersamaan
  • Saat di sentuh menyeret dari item A ke B, jika capture tetap dipertahankan maka event move akan dikirim ke A, jadi untuk menyorot B mungkin perlu melepas capture

Toggle Paint dan perluasan status drag

  • Di Rounded Cell Painter, pointerdown menangkap warna cat awal
  • Setelah itu, semua pointermove hingga pointerup menggunakan warna cat yang sama
  • Untuk itu, state.dragging harus mencakup bukan hanya x, y awal tetapi juga warna cat awal
  • Demonya ada di examples page

Kasus tepi lostpointercapture

  • lostpointercapture dapat digunakan untuk mendeteksi apakah pointer capture telah hilang
  • Dalam situasi apa ini terjadi dan apa yang harus dilakukan masih belum sepenuhnya dirapikan
  • Skenario yang dibahas di komentar adalah sebagai berikut
    • Mulai menyeret lingkaran dengan tombol kiri
    • Pindah ke luar area drag sambil tetap menahan tombol kiri
    • Tekan tombol kanan lalu lepaskan tombol kiri
    • Klik di mana saja di luar area drag
  • standard menyatakan bahwa setelah pointer capture dilepas, user agent wajib memicu event lostpointercapture
  • Perilaku browser nyata sangat beragam; pada beberapa sistem lostpointercapture terjadi, tetapi pada sistem lain tidak terjadi sampai mengklik tempat lain

1 komentar

 
GN⁺ 2023-09-30
Komentar Hacker News
  • Ini tulisan tentang drag, dan saya mengalami hampir semua jebakan yang disebut Amit lalu sampai pada solusi yang sama. Tulisan yang bagus
    Dari sisi pengguna, mengubah urutan dengan drag terlihat sangat natural, tetapi begitu masuk ke item dengan ukuran berbeda-beda, membuat placeholder di antara item, deteksi tepi, sampai lubang kelinci algoritma penyesuaian kotak, ini termasuk cukup sulit untuk diimplementasikan sendiri

    • Ada trik yang cukup umum yang saya suka untuk kasus seperti ini: ketika pengguna mulai melakukan drag, hitung semua target drop yang mungkin, atau layout dari target yang sedang terlihat, lalu catat posisi akhir objek yang sedang di-drag pada tiap layout
      Setiap kali mouse bergerak, pilih posisi yang paling dekat dengan posisi objek yang sedang di-drag saat ini, lalu render layout yang dipilih. Ini bekerja dengan rasa yang baik bahkan pada layout atau reflow yang kompleks
    • Saya harus memecahkan masalah ini di aplikasi kartu indeks saya, Card Buddy. [1] Itu tantangan yang menarik, dan belakangan saya juga menemukan cara yang lebih baik
      Pada akhirnya, ketika sebuah kartu diangkat, saya menghitung layout seolah-olah kartu itu dihapus dari board, dan itu membuatnya lebih mudah. Tinggal mendorong item-item yang berada di posisi yang diarahkan mouse
      Meski begitu, penanganan kasus khususnya sangat banyak, terutama ketika mulai mengedit kartu baru yang belum “di-commit” ke model data. Saya juga harus menambahkan opsi untuk mendorong kartu yang ada agar membuat tempat untuk kartu hantu
      Mengakui bahwa ada banyak kasus khusus yang harus ditangani secara manual itu membantu. Jika mendekatinya seolah ada solusi yang lebih umum dan seragam, desainnya akan berputar-putar. Suatu hari saya mungkin harus merangkum berbagai kasus khusus itu dalam tulisan blog
      Saya juga menemukan cara layout yang lebih baik, yang mengurangi perhitungan yang tidak perlu dan membuat layout engine lebih fleksibel serta lebih ramah pengguna. Mengejutkan betapa besar perbedaan yang dibuat oleh pilihan representasi model data terhadap solusinya
      [1] https://www.ussherpress.com/cardbuddy/
    • Kalau pengubahan urutan dengan drag ini sampai mendukung nesting, itu jadi lebih sulit lagi
    • Saya sependapat. Idenya sederhana, tapi implementasinya bisa brutal. Seleksi dengan drag juga selama bertahun-tahun saya anggap terlalu vanzelfsprekend
  • Sepertinya tulisan itu tidak membahas penanganan pembatalan. Misalnya di Windows ada konvensi bahwa menekan Escape membatalkan drag
    Kadang kita juga ingin membatalkan drag jika mouse dilepas di luar area yang ditentukan. Pembatalan menjadi cara undo yang lebih cepat bagi pengguna, atau jalan keluar seperti “ah, ternyata saya tidak bermaksud melakukan drag”
    Jadi meski tidak ada fitur undo terpisah, kita harus menyimpan status awal pada saat drag dimulai, lalu memulihkannya jika dibatalkan
    Untuk pembatalan drag di luar area, ada juga membatalkan pembatalan. Saat pointer berada di luar area, secara visual tampilkan seolah sudah kembali ke keadaan semula, lalu ketika pointer masuk lagi ke dalam area, lanjutkan drag. Pembatalan yang sebenarnya terjadi saat mouse dilepas, tetapi selama drag pun pengguna sudah diberi petunjuk visual bahwa aksi itu akan dibatalkan

    • Di Windows, jika Anda menyeret sesuatu terlalu jauh, objek yang sedang di-drag dan pointer tiba-tiba menghilang, lalu tanpa peringatan mental kembali ke posisi semula
      Sulit dipercaya Microsoft masih menganggap ini desain UI yang baik. Ini benar-benar membingungkan pengguna; Anda bisa melihat orang takut melepas tombol kiri mouse karena tidak tahu apa yang terjadi, atau mengulanginya seperti yo-yo
      Saya setuju bahwa pembatalan adalah masalah besar yang sering terlewatkan, dan tidak sederhana. Pengguna umum juga tidak tahu bahwa ESC bisa membantu
    • Saya penasaran apakah di platform lain ada tombol pembatalan selain ESC yang perlu ditangani
  • Ada detail tambahan yang saya pelajari selama 20 tahun menyeret berbagai macam objek di GUI Ardour [0]: semua event tombol ditekan dan dilepas harus diperlakukan sebagai event drag tanpa gerakan
    Artinya, penekanan selalu memulai drag dan pelepasan mengakhirinya, sementara kode yang menangani pelepasan melakukan perlakuan khusus untuk kondisi tanpa gerakan
    [0] https://ardour.org/

    • Cara itu mungkin bekerja, tetapi saya tidak melakukannya begitu, dan setelah hal-hal dasar ditangani dengan benar, tidak ada masalah
      Saya punya handler mousedown dan mouseup biasa, lalu memakai timeout yang dapat dikonfigurasi sekitar 150ms untuk menentukan apakah itu klik, awal drag, atau tindakan lain. Jika mouseup terjadi atau mouse keluar dari layar dan ada drag yang sedang berlangsung, saya menjalankan stopdrag. Bagi saya, drag hanyalah salah satu dari beberapa kasus khusus
    • Saya penasaran mengapa melakukannya dengan cara itu
  • Dahulu sekali di Minneapolis ada sebuah geometry center yang didukung NSF. Ada konferensi teori grup komputasional, dan mereka mengundang beberapa orang dari bidang sekitar sebagai semacam maskot
    Saya diundang karena menulis sistem geometri aljabar, lalu bangun sangat pagi dalam cuaca -20°F untuk mendapatkan workstation Silicon Graphics yang akan dipakai hari itu, kemudian mengodekan sebuah gim untuk lebih memahami generator dan relasi grup
    Gim itu memakai drag. Hasilnya, percakapan yang berlanjut sampai pukul 2 pagi terasa menyenangkan. Gagasan saya adalah drag tidak harus mengikuti hukum fisika dunia nyata, dan drag seharusnya terasa seperti satu tab LSD yang sangat kuat. Semua orang larut di dalamnya dan masing-masing mengajukan ide

  • Artikel ini, jika mengesampingkan berbagai pengecualian yang muncul di peramban web, mengimplementasikan drag yang relatif mendasar. Saya penasaran apakah ada materi tentang drag yang memiliki batasan seperti snap ke guideline, mencegah tabrakan dengan batas atau objek lain, atau target drop beranimasi yang ukuran atau posisinya berubah sebagai respons terhadap operasi drag
    Dulu saya ingin membuat UI timer Pomodoro kustom yang membagi jam berbentuk lingkaran menjadi potongan-potongan irisan dengan panjang berbeda untuk mendefinisikan interval fokus/istirahat. Implementasi pengubahan urutan drag untuk potongan-potongan irisan itu tidak banyak mengalami kemajuan

    • Dalam pendekatan kode ini, gerakan drag memperbarui suatu state. Saat menetapkan state, constraint bisa diterapkan, lalu state itu kembali menggerakkan tampilan
      Saya ingin memisahkan sistem constraint dari sistem penanganan event. Library seperti jquery-ui mengikat keduanya, sehingga sistem penanganan event harus mengetahui semua constraint yang mungkin. jquery-ui mendukung bounding box, axis, grid persegi, dan snap ke elemen DOM. Namun jika ingin snap ke grid heksagonal, grid berskala log, atau batas melingkar, itu tidak didukung
      Di kode terlihat state.pos = …, dan di situlah state ditetapkan. Untuk constraint, pos disembunyikan di balik setter. Dengan begitu, meski kode penanganan event drag tidak tahu constraint apa yang diperlukan, setter bisa menerapkan constraint tersebut
      Sepertinya saya perlu memperbarui halaman untuk menunjukkan contoh constraint. Saya benar-benar lupa menyebut aspek ini dalam resep kode
      Contoh constraint lama ada di https://www.redblobgames.com/articles/curved-paths/making-of..., dan contoh pencegahan tabrakan ada di https://redblobgames.github.io/circular-obstacle-pathfinding.... Namun saya belum mencoba pengubahan urutan drag atau target drop beranimasi
  • Saya juga berterima kasih pada situs ini. Saat merancang sistem heksagonal untuk analisis geospasial di Uber, saya banyak merujuk ke https://www.redblobgames.com/grids/hexagons/

  • Ini artikel yang merangkum dengan baik jebakan dan hal-hal yang perlu diperhatikan saat menangani interaksi yang layak
    Untuk sisi yang lebih “siap pakai”, saya sudah cukup lama menggunakan interactjs di beberapa proyek

  • Terima kasih semuanya. Saya terkejut ini muncul di HN hari ini, dan saya bisa menjawab pertanyaan

    • Saya penasaran apakah Anda sudah melihat atribut bawaan HTML draggable="true" dan event drag
      Saya juga penasaran apa kekurangan fitur bawaan itu dibandingkan dengan kode ini
      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...
    • Ini diskusi yang sangat bagus tentang drag di JavaScript, dan informasinya benar-benar berguna
      Ada pertanyaan singkat. Saya penasaran bagaimana cara membatasi gerakan drag ke satu axis dengan event DOM bawaan seperti dragstart. Saya sudah mengimplementasikan fitur drag-and-drop dengan event dragstart/dragenter/dragover/drop, tetapi belum menemukan cara cepat untuk membatasi gerakan ke sumbu x. Seingat saya API drag-and-drop JQuery mendukung ini. Saya berusaha hanya memakai event/API DOM native, jadi akan senang jika ada informasi atau petunjuk terkait
    • Dulu saya selalu menulis kode UI peramban, tetapi sempat berhenti beberapa tahun. Ini pertama kalinya saya mendengar seseorang memakai pointer event
      Mengingat Safari baru menambahkan dukungannya pada 2020, saya penasaran apakah Anda tidak khawatir peramban tidak mendukungnya. Pengguna macOS Mojave pun mungkin memakai Safari 13 atau 14, jadi saya berharap Safari 12 secara praktis sudah tidak dipakai lagi, tetapi jika fitur sesederhana drag tidak berjalan di aplikasi produksi yang ditujukan untuk pengguna web umum, itu akan cukup buruk
      Menambahkan event handler ke document saat drag adalah praktik lama, dan peramban terus menambahkan fitur baru untuk menyederhanakan use case tertentu, tetapi masing-masing punya pengecualian dan jebakan. Artikel ini juga mengatakan belum membahas semuanya sepenuhnya. Hasil akhirnya pun masih menyisakan kombinasi pointer event dan touch event
      Saya penasaran apakah “kesederhanaan” dalam arti berkurangnya kode cukup bernilai untuk menanggung biaya berupa pengecualian tambahan, dukungan peramban yang lebih rendah, serta kebutuhan pengembang untuk memahami detail dan perbedaan peramban pada pointer event yang kemungkinan lebih kurang dipahami dan terdokumentasi dibanding mouse event
  • Ada satu hal yang terlewat: aksesibilitas. Bagaimana cara membuat drag bisa dilakukan dengan kontrol keyboard?

    • Rasanya ini pertanyaan yang agak melenceng. Ini mirip bertanya bagaimana pengguna keyboard di program gambar bisa melakukan tindakan menahan klik untuk melakukan panning kanvas. Tidak begitu caranya. Melakukan panning dengan mouse atau menyeret objek hanyalah salah satu antarmuka; tugasnya adalah panning kanvas, dan ada banyak cara untuk mencapainya
      Dalam contoh saya, jawaban untuk aksesibilitas adalah menyediakan kontrol keyboard tambahan untuk menyelesaikan tugas tersebut. Kalau bisa, sebaiknya dengan cara yang tidak mengharuskan tombol terus ditekan. Misalnya, masuk ke mode panning lewat shortcut, lalu memindahkan viewport di sekitar kanvas dengan tombol panah—itu menyelesaikan masalah
      Untuk objek yang bisa di-drag, tugasnya adalah mengubah urutan. Agar ini aksesibel, cukup sediakan cara alternatif untuk mengubah urutan. Misalnya, gunakan TAB untuk memutar fokus di antara objek, pilih objek yang sedang fokus dengan tombol, lalu gunakan tombol panah untuk memindahkan pratinjau drag ke posisi valid terdekat ke arah tersebut
    • Secara umum jawabannya lebih dekat ke “jangan lakukan seperti itu”. UI harus menyediakan alternatif untuk drag agar orang yang tidak bisa menggunakan perangkat pointer atau sentuh tetap bisa berinteraksi dengan halaman
      Dengan kata lain, jangan memaksakan dukungan keyboard ke dalam implementasi drag; tambahkan fungsi keyboard yang bermakna secara terpisah dari fitur drag
    • Itu akan menjadi objek yang bisa dipindahkan, dan sepertinya mudah dibuat berdasarkan implementasi ini
    • Pilih item dengan TAB / Space / Enter, lalu gerakkan elemen yang “sedang di-drag” dengan Ctrl + tombol panah. Tentu saja, setiap satu kali menekan tombol hanya bisa bergerak satu blok
  • Bagus. Awal minggu ini saya sedang mengimplementasikan drag elemen SVG di aplikasi Vue, dan saya harus menemukan sendiri hampir semua hal yang dijelaskan penulis dalam urutan yang sama; hasil akhirnya juga menjadi komponen yang hampir sama
    Hanya saja, alih-alih komponen fungsional, saya menulisnya sebagai utilitas composable bernama useDragging

    • Keren. Saya sudah mencoba directive dan komponen dengan slot, tetapi belum mencoba composable. Soalnya saya belum yakin bagaimana harus menyiapkan event handler dengan pendekatan itu. Pengalaman saya dengan composable juga belum banyak