4 puan yazan GN⁺ 2023-09-30 | 1 yorum | WhatsApp'ta paylaş
  • Red Blob Games’in etkileşimli diyagramlarında, okur nesneleri doğrudan hareket ettirdiğinde ekranın anında tepki vermesi gerekir; bu yüzden giriş işleme Pointer Events merkezli düzenlenerek fare ve dokunma birlikte ele alınır
  • Eski mousedown/mouseup/mousemove yaklaşımında, işaretçi nesnenin dışına çıktığında veya düğme dışarıda bırakıldığında sürükleme durumu karışabildiği için, genel işleyiciler gibi ek önlemler gerekiyordu
  • Dokunma girdisinde olay iletimi fareden farklıdır ve tek parmakla sürükleme sayfanın kaymasına da yol açtığından, touchstart içinde preventDefault() çağrılarak çakışma azaltılır
  • Nihai tarif, pointerdown, pointermove, pointerup, pointercancel ve setPointerCapture() birleşimini kullanır; ayrıca nesnenin aniden sıçramaması için sol düğme ile başlangıç ofseti kontrol edilir
  • Metin/görsel içeren nesneler, çoklu dokunma, iç içe sürükleme, <canvas>, hover ile drag birleşimi ve lostpointercapture gibi platforma özgü istisnalar gerçek arayüzlerde ayrıca ele alınmalıdır

Sürükleme giriş işleyicisinin hedefi

  • Birçok etkileşimli sayfada, okurun doğrudan hareket ettirdiği sürüklenebilir nesneler bulunur ve diyagramın nesne konumundaki değişime tepki vermesi gerekir
  • Amaç, hem fare hem de dokunma girdisinde çalışan bir giriş olayı işleyicisi oluşturmaktır
  • Temel akış, pointerdown ile sürüklemeyi başlatmak, pointermove ile konumu güncellemek ve pointerup ya da iptal olayında sürüklemeyi bitirmekten oluşur
  • Gerçek uygulamada fare düğmeleri, çok parmaklı dokunma, olayın iletildiği hedef ve sağ tık bağlam menüsü yüzünden yapı basit modelden daha karmaşık hale gelir
  • Temel tarif şu öğeleri birleştirir
    • pointerdown içinde event.button !== 0 ise dönerek yalnızca birincil düğmeyi işleme
    • Olay koordinatlarını diyagram koordinatlarına dönüştürme
    • Başlangıçta nesne konumu ile işaretçi konumu arasındaki fark olan dx, dy değerlerini saklama
    • el.setPointerCapture(event.pointerId) ile işaretçiyi yakalama
    • pointermove içinde kayıtlı ofseti ekleyerek state.pos değerini güncelleme
    • pointerup, pointercancel içinde state.dragging = null
    • touchstart içinde preventDefault() çağırma
  • Bu tarif bir giriş olayı işleyicisidir; gerçek çıktı işleme mantığının ayrı bir durum işleyicisine bağlanması gerekir
  • Tam kod examples page üzerinde yer alır; burada nesne taşımaya ek olarak sayıları sağa sola sürükleme, canvas boyama ve kısıtlı sürükleme örnekleri de bulunur
  • Test kapsamı; Gecko/Firefox, Blink/Chrome, WebKit/Safari ile Mac, Windows, Linux, Android, iPhone ve iPad’i içerir
  • Hover yapabilen stylus, hibrit dokunma+fare cihazları ve sesli giriş test edilmemiştir
  • Bu yaklaşım, bir öğeyi başka bir öğenin üstüne bırakmak için kullanılan HTML Drag and Drop API değildir; diyagramlarda fare ve dokunma olaylarını doğrudan okumak gerekir

Yalnızca fare olaylarını kullanmanın sınırları

  • İlk uygulama, mousedown, mouseup, mousemove olaylarını doğrudan sürüklenen öğeye bağlama yöntemiydi
  • Yüzeyde çalışıyor gibi görünse de iki sorun ortaya çıkıyordu
    • İşaretçi hızlı hareket edince artık çemberin üstünde olmayabiliyor ve olayları alamıyordu
    • Düğme çemberin dışında bırakılırsa dragging durumunda takılı kalınabiliyordu
  • İyileştirme olarak mousedown çemberden alınırken, sürükleme sırasında mousemove ve mouseup olayları geçici olarak document üzerine ekleniyordu
  • mouseup içinde document üzerine eklenen mousemove, mouseup işleyicileri kaldırılınca fareyle sürükleme daha kararlı çalışıyordu
  • Bu yaklaşım yalnızca fareyi ele alır; dokunma olaylarını kapsamaz

Dokunma olaylarının farkları

  • Dokunma girdisi, mousedown, mouseup, mousemove yerine touchstart, touchend, touchmove kullanır
  • Dokunma olayları touchstart sonrasında otomatik olarak capture edilir; böylece tüm touchmove olayları özgün öğeye iletilir
  • Bu özellik sayesinde, fare olaylarında olduğu gibi document üzerine geçici genel olay işleyicileri eklemek gerekmez
  • Tarayıcı dokunma dizisini iptal etmek zorunda kaldığında touchcancel gönderir
  • Dokunma sürükleme uygulamasında touchstart içinde preventDefault() çağrılır ve touchmove içinde event.changedTouches[0] koordinatlarıyla konum güncellenir
  • Bu yaklaşım yalnızca dokunmayı ele alır; fare olaylarını ele almaz

Pointer Events ile fare ve dokunmayı birleştirmek

  • Hem fareyi hem dokunmayı desteklemek için çok sayıda olay işleyicisi gerekiyordu ve 2021’den önce bu şekilde çalışılıyordu
  • 2011~2014 arasında d3 projelerinde d3-drag kullanıldı; d3 kullanılmayan projelerde ise ayrı fare+dokunma kodu yazıldı
  • Pointer Events, fare ve dokunma işlemeyi birleştirip sadeleştirir
    • MS IE, desteği 2012’de ekledi
    • Chrome 2017’de, Firefox 2018’de, Safari 2020’de destek ekledi
  • Tarayıcı davranışı zaman içinde değişti; 2017’de Chrome bazı olayları varsayılan olarak passive mode durumuna getirince, nesne sürüklemeye çalışırken sayfanın kayması sorunu ortaya çıktı
  • Safari ve Firefox da 2018’de aynı değişikliği uyguladı
  • pointer capture kullanıldığında, fare olaylarında gereken document genel işleyicilerini ekleyip kaldırmadan daha basit mantıkla işlem yapılabilir

Pointer Capture ile işaretçi takibini sürdürmek

  • Pointer capture, işaretçi çemberin, diyagramın ya da tarayıcı penceresinin dışına çıksa bile takibin sürmesini sağlar
  • Fare olaylarında aynı etki için document üzerine olay işleyicileri eklemek gerekirken, Pointer Events tarafında setPointerCapture(event.pointerId) daha basittir
  • İşaretçi yakalama yoksa, hızlı sürüklemede ya da diyagramın dışına çıkıp geri dönüldüğünde sürükleme durabilir
  • Düğmenin diyagram dışında bırakılması ya da Alt+Tab ile başka pencereye geçilmesi gibi durumlarda da sürüklemenin sonlandırılması, capture olup olmamasına göre değişir
  • Dokunmatik cihazlarda pointer capture varsayılan davranıştır, ancak farede açıkça ayarlanmalıdır

Dokunma kaydırması ve preventDefault()

  • Dokunmatik cihazlarda tek parmakla sürükleme varsayılan olarak sayfanın kaymasına neden olur
  • Aynı tek parmak sürüklemesi çemberi de hareket ettirirse kaydırma ve nesne sürükleme aynı anda gerçekleşir
  • En basit çözüm, diyagrama CSS touch-action: none uygulamaktır; ancak bu durumda diyagramın hiçbir yerinde kaydırma yapılamaz
  • İstenen davranış, yalnızca çember sürüklenirken kaydırmayı engellemek; diyagramın kendisi sürüklenirken ise kaydırmaya izin vermektir
  • Karşılaştırma sonuçları şöyledir
    • Tüm diyagrama touch-action: none: hem çember sürüklemede hem diyagram sürüklemede kaydırma durur
    • Varsayılan değer: ikisinde de kaydırma engellenemez
    • Yalnızca çembere touch-action: none: beklendiği gibi çalışmaz
    • touchstart içinde preventDefault(): yalnızca çember sürüklerken kaydırmayı engeller, diyagram kaydırmasına izin verir
  • preventDefault() pointerstart yerine touchstart üzerine eklenir
  • Bu yaklaşım çoğu durumda çalışır; ancak masaüstü Firefox’un “mobile emulation mode”unda çalışmadı, Android’deki mobil Firefox’ta ise çalıştı

Sürükleme başlangıç ofsetini kaydetme

  • Nesnenin kenarından tutup sürüklerken nesnenin merkezinin işaretçi konumuna aniden atlaması kullanım hissini kötüleştirir
  • Çözüm, sürükleme başlangıcında nesne merkezi ile işaretçi konumu arasındaki göreli konumu saklamaktır
  • Sonrasında hareket ederken saklanan dx, dy değerleri mevcut işaretçi koordinatlarına eklenerek nesne konumu hesaplanır
  • Bu yöntem state.dragging değerini basit bir true/false yerine {dx, dy} gibi göreli konum bilgisine dönüştürür
  • Farede kenardan tutulduğunda sıçrama farkı belirgin biçimde görünür; dokunmada ise parmak kapattığı için daha az fark edilebilir
  • Bununla ilgili küçük ayrıntılar little details sayfasında da derlenmiştir

Bağlam menüsü ve fare düğmeleri

  • Bağlam menüsünü etkinleştirme şekli platforma göre değişir
    • Windows: sağ tık down+up, Shift+F10
    • Linux: sağ düğme down, Shift+F10
    • Mac: sağ düğme down, Ctrl+sol tık
    • iOS: yalnızca metinde long press
    • Android: her yerde long press
  • Sorun şu ki pointerdown görünürken pointerup her zaman görünmeyebilir
  • Düğme gerçekte bırakılmış olsa bile kod tarafında hâlâ basılı kabul edilebilir
  • Buna karşılık olarak dragging durumunu yalnızca sol fare düğmesi için ayarlamak gerekir; sağ düğme yok sayılır
  • Pointer Events spesifikasyonundaki button özelliğinde button = 0, primary button anlamına gelir; bu da orta ve sağ düğmeleri hariç tutmak için kullanılabilir
  • Mac'te Chrome ve Safari'de Ctrl+tık, sol düğme pointerdown gibi gelebileceğinden event.ctrlKey de kontrol edilmelidir
  • Seçenekler şunlardır
    • contextmenu içinde preventDefault() ile menü engellenebilir, ancak bu varsayılan orta tık veya sürükleme davranışıyla çakışabilir
    • Yalnızca sol düğmeyle sürükleme yapmak çoğu sağ tık farkını önleyebilir
    • Mac'teki Ctrl+tık durumunu da önlemek için ctrlKey de hariç tutulmalıdır
  • Düğme değişimi pointerdown ya da pointerup ile iletilmese bile pointermove sırasındaki düğme durumundan kısmen anlaşılabilir

Metin ve görsel içeren nesneler

  • Sürüklenebilir öğenin içinde metin veya görsel varsa ek işlem gerekir
  • Bir çember sürüklenirken içindeki metin seçilebilir
  • Çözüm CSS user-select: none uygulamaktır
    • Her zaman uygulanırsa iç metin hiçbir zaman seçilemez
    • Yalnızca sürükleme sırasında uygulanırsa normalde metin seçimi mümkün olur
  • 2023 başı itibarıyla Safari, öneksiz user-select özelliğini hâlâ desteklemediği için webkitUserSelect de birlikte ayarlanmalıdır
  • Windows, Linux ve Mac; metin ve görseller için uygulamalar arası drag and drop destekler ve bu, nesne sürüklemeyle çakışır
  • dragstart içinde preventDefault() çağırmak, seçili metin veya görsel sürüklemesindense nesne sürüklemesine öncelik verebilir
  • Davranış tarayıcıya ve işletim sistemine göre biraz değişir

Birden fazla işaretçi ve eşzamanlı sürükleme

  • Birden fazla parmakla veya birden fazla fareyle birden fazla nesneyi aynı anda sürükleyebilmek bir edge case'tir
  • Dokunmada verilen kod, birden fazla nesnenin aynı anda sürüklenmesinde zaten çalışır
  • Aynı nesnenin iki parmakla sürüklenmesi durumu için ayrı işlem gerekir
  • Çözüm, pointerdown sırasında event.pointerId değerini state.dragging içine veya duruma kaydetmek ve pointermove sırasında aynı pointerId değilse yok saymaktır
  • Bu uygulama örneği metne tamamen uygulanmış değildir; canvas dragging test içinde görülebilir
  • Fare için Pointer Events spesifikasyonu, genel işletim sistemleri ve kullanıcı aracıların çoğunlukla birden fazla fare girdisi kavramına sahip olmadığını belirtir
  • Trackpad ve harici fare birlikte bağlı olsa bile genelde tek bir fare işaretçisi olarak ele alındıklarından, farklı farelerle farklı nesneleri sürüklemenin mümkün olmadığı değerlendirilir

Birden fazla fare düğmesine aynı anda basıldığında

  • Birden fazla fare düğmesine aynı anda basıldığında Pointer Events ile Mouse Events farklı davranır
  • Mouse Events, her düğme basışı için mousedown, her düğme bırakılışı için mouseup gönderir
  • Pointer Events spesifikasyonuna göre pointerdown ilk basılan düğmede, pointerup ise en son bırakılan düğmede oluşur
  • Bu nedenle pointerdown ve pointerup farklı düğme durumlarında meydana gelebilir
  • Örneğin sol düğmeye basıp ardından sağ düğmeye basar, sonra sol düğmeyi bırakırsanız nesne sürükleme durumunda kalmaya devam edebilir
  • Çözüm, pointermove içinde event.buttons & 1 ile sol düğmenin durumunu kontrol etmek ve artık basılı değilse end(event) çağırmaktır
  • Pointer capture, tüm düğmeler bırakılana kadar sürebilir ve açıkça serbest bırakılmazsa devam edebilir
  • Bu uygulama bu sorunu ve diğer birçok edge case'i tamamen ele almaz

İç içe sürüklenebilir öğeler

  • Sürüklenebilir bir öğenin içinde başka bir sürüklenebilir öğe varsa hem içteki hem dıştaki öğe sürüklemeyi işler
  • Çözüm, içteki sürükleme öğesinde event.stopPropagation() çağırarak olayın dıştaki sürükleme öğesine yükselmesini engellemektir
  • Bunun için hem pointerdown hem de pointermove sırasında yayılım durdurulur
  • Demo metinde yoktur, ancak Vue playground örneğinde kırmızı sürüklenebilir öğenin sarı sürüklenebilir öğenin çocuğu olduğu örnek görülebilir

Canvas'ta sürükleme

  • Genellikle SVG kullanılır, ancak <canvas> içinde sürüklenebilir öğenin kendisine olay işleyicileri veya fare imleci biçimi atanamaz
  • 2D Canvas veya WebGL'de olay işleyicileri <canvas> öğesine eklenmeli ve koordinatların sürüklenebilir nesnenin üzerinde olup olmadığı doğrudan belirlenmelidir
  • İşleme yöntemi şöyledir
    • pointerdown, touchstart, dragstart: sürüklenebilir nesnenin üzerinde değilse hemen geri dön
    • pointermove: sürüklenebilir nesnenin üzerinde olup olmamasına göre imleci ayarla
  • Demo examples page üzerinde bulunuyor

Fare hover ve dokunmatik drag birleşimi

  • Bazı diyagramlar farede düğmeye basılmadan hover ile tepki verir; dokunmatik cihazlarda ise hover olmadığı için drag ile tepki vermelidir
  • Temel tarif, hem fare hem de dokunmatik için drag bekler
  • Fare hover ve dokunmatik drag'i birlikte kullanmak için pointermove içinde if (!state.dragging) koşulu kaldırılır
  • Responsive Design page üzerinde fareyle satırın üzerine hover yapıldığında yerleşim değişir, dokunmatikte ise drag ile yerleşim değişir
  • Hexagons Guide içinde de birçok diyagram fare hover ve dokunmatik drag'i birlikte kullanır
  • Dokunmatikte A öğesinden B'ye sürüklerken capture korunursa move olayı A'ya iletilir; bu yüzden B'yi vurgulamak için capture'ın bırakılması gerekebilir

Toggle Paint ve drag durumunun genişletilmesi

  • Rounded Cell Painter içinde pointerdown başlangıç boya rengini yakalar
  • Sonrasında pointerup olana kadar tüm pointermove olayları aynı boya rengini kullanır
  • Bunun için state.dragging, yalnızca başlangıç x ve y değil, başlangıç boya rengini de içermelidir
  • Demo examples page üzerinde bulunuyor

lostpointercapture uç durumları

  • lostpointercapture, pointer capture'ın kaybedilip kaybedilmediğini algılamak için kullanılabilir
  • Hangi durumlarda oluştuğu ve ne yapılması gerektiği henüz tamamen netleşmiş değil
  • Yorumlarda ele alınan senaryolar şunlardır
    • Sol düğmeyle daireyi sürüklemeye başla
    • Sol düğme basılıyken sürükleme alanının dışına çık
    • Sağ düğmeye basıp ardından sol düğmeyi bırak
    • Sürükleme alanı dışında herhangi bir yere tıkla
  • standard, pointer capture bırakıldıktan sonra kullanıcı aracısının mutlaka lostpointercapture olayını tetiklemesi gerektiğini söyler
  • Gerçek tarayıcı davranışı büyük ölçüde farklılık gösterir; bazı sistemlerde lostpointercapture tetiklenirken bazılarında başka bir yere tıklanana kadar tetiklenmez

1 yorum

 
GN⁺ 2023-09-30
Hacker News yorumları
  • Sürüklemeyi ele alan bir yazı; Amit'in bahsettiği tuzakların neredeyse hepsini yaşadım ve aynı çözüme ulaştım. Harika bir yazı.
    Kullanıcı açısından sürükleyerek sıralamayı değiştirme çok doğal görünür, ama farklı boyutlarda öğeler, öğeler arasında yer tutucu oluşturma, kenar algılama ve kutu yerleştirme algoritmalarının tavşan deliğine girince, bunu kendiniz uygulamak epey zor işler arasına girer.

    • Böyle durumlarda sevdiğim, oldukça genel bir numara var: Kullanıcı sürüklemeye başladığında olası tüm bırakma hedeflerinin ya da o anda görünen hedeflerin yerleşimini hesaplayın ve her yerleşimde sürüklenen nesnenin nerede sonlanacağını kaydedin.
      Fare her hareket ettiğinde bu konumlar arasından mevcut sürüklenen nesne konumuna en yakın olanı seçip, seçilen yerleşimi render etmek yeterli. Karmaşık yerleşimlerde veya reflow durumlarında bile hissiyatı iyi çalışır.
    • Bu sorunu indeks kartı uygulamam Card Buddy'de çözmem gerekti. [1] Eğlenceli bir meydan okumaydı ve sonrasında daha iyi bir yol da buldum.
      Sonunda, kartı elime aldığımda o kart panodan silinmiş gibi yerleşimi hesapladım; böylece iş kolaylaştı. Tek yapmanız gereken, farenin üzerinde bulunduğu konumdakileri itmekti.
      Yine de istisna işleme inanılmaz fazlaydı; özellikle de veri modeline henüz “commit” edilmemiş yeni bir kartı düzenlemeye başladığınızda zordu. Mevcut kartları iterek bir hayalet kart yeri oluşturma seçeneği de eklemem gerekti.
      Elle ele alınması gereken çok sayıda istisna olduğunu kabul etmek işe yarıyor. Daha genel ve homojen bir çözüm varmış gibi yaklaşırsanız tasarımda dönüp durursunuz. Bir gün çeşitli istisna durumlarını bir blog yazısında derlemem gerekecek gibi.
      Daha iyi bir yerleşim yöntemi de buldum; gereksiz hesaplamaları azaltıyor ve yerleşim motorunu daha esnek, kullanıcı dostu hâle getiriyor. Veri modeli temsilini nasıl seçtiğinizin çözüme bu kadar büyük fark yaratması şaşırtıcı.
      [1] https://www.ussherpress.com/cardbuddy/
    • Buna iç içe geçmeyi de destekleyen sürükleyerek sıralama eklenirse iş daha da zorlaşıyor.
    • Aynı fikirdeyim. Fikir basit, ama uygulama acımasız olabiliyor. Sürükleyerek seçmeyi de yıllarca fazla doğal karşıladım.
  • Yazı iptal işlemeyi ele almıyor gibi. Örneğin Windows'ta Escape'e basınca sürüklemenin iptal edilmesi şeklinde bir teamül var.
    Bazen fareyi tanımlı alanın dışında bırakırsanız sürüklemeyi iptal etmek de isteyebilirsiniz. İptal, kullanıcıya daha hızlı bir geri alma ya da “aa, aslında sürüklemek istemiyormuşum” türünden bir kaçış yolu sağlar.
    Bu yüzden ayrı bir geri alma özelliği olmasa bile, sürükleme başlangıcındaki özgün durumu saklayıp iptal edilirse geri yüklemek gerekir.
    Alan dışına sürükleyerek iptal durumunda iptali geri alma da vardır. İşaretçi alanın dışındayken görsel olarak özgün duruma dönmüş gibi gösterip, işaretçi tekrar alanın içine girdiğinde sürüklemeyi sürdürme yaklaşımıdır. Gerçek iptal fare bırakıldığında gerçekleşir, ama sürükleme sırasında da iptal edileceği önceden görsel olarak bildirilmiş olur.

    • Windows'ta bir şeyi çok uzağa sürüklediğinizde, sürüklenen nesne ve işaretçinin birden kaybolup hiçbir uyarı olmadan özgün konumuna geri fırladığı bir davranış var.
      Microsoft'un hâlâ bunu iyi bir UI tasarımı sandığına inanamıyorum. Kullanıcının kafasını tamamen karıştırıyor; ne olduğunu anlamayıp sol fare düğmesini bırakmaktan korkan ya da yo-yo gibi tekrar tekrar deneyen insanlar görebilirsiniz.
      İptalin sıkça gözden kaçan büyük bir sorun olduğuna ve basit olmadığına katılıyorum. Sıradan kullanıcı ESC'nin işe yaradığını bile bilmez.
    • Diğer platformlarda ESC dışında ele alınması gereken ayrı iptal tuşları olup olmadığını merak ediyorum.
  • Ardour [0]'un GUI'sinde her tür nesneyi sürükleyerek geçirdiğim 20 yılda öğrendiğim ek bir ayrıntı var: Tüm düğmeye basma ve bırakma olaylarını, hareket içermeyen sürükleme olayları olarak ele almak gerekir.
    Yani basma her zaman sürüklemeyi başlatır ve bırakma ile biter; bırakmayı işleyen kod da hareketsizlik koşulunu özel olarak ele alır.
    [0] https://ardour.org/

    • O yöntem de çalışır, ama ben öyle yapmadım ve temelleri doğru oturttuktan sonra sorun yaşamadım.
      Normal mousedown ve mouseup işleyicilerim var; bunun tıklama mı, sürükleme başlangıcı mı yoksa başka bir eylem mi olduğuna karar vermek için yaklaşık 150 ms'lik yapılandırılabilir bir timeout kullanıyorum. mouseup gerçekleşirse ya da fare ekranın dışına çıktıysa ve devam eden bir sürükleme varsa stopdrag yapıyorum. Benim için sürükleme, birçok özel durumdan yalnızca biri.
    • Bunu neden yaptığını merak ediyorum.
  • Çok uzun zaman önce Minneapolis'te NSF tarafından desteklenen bir geometry center vardı. Hesaplamalı grup teorisi konferansı düzenlenmişti ve çevre alanlardan maskot gibi birkaç kişi davet edilmişti.
    Bir cebirsel geometri sistemi yazdığım için davet edilmiştim; -20°F soğukta çok erken kalkıp o gün kullanacağım Silicon Graphics iş istasyonunu kaptıktan sonra grup üreteçlerini ve ilişkilerini daha iyi anlamaya yönelik bir oyun kodladım.
    Oyunda sürükleme vardı. Sonuçta gece 2'de süren güzel sohbetler çıktı. Benim fikrim, sürüklemenin gerçek dünyanın fizik yasalarına uymak zorunda olmadığıydı; sürükleme çok güçlü bir LSD pulu gibi hissettirmeliydi. Herkes buna kapıldı ve kendi fikirlerini ortaya attı.

  • Yazı, web tarayıcılarında ortaya çıkan çeşitli istisnaları bir kenara bırakırsak, nispeten temel bir sürükleme uygulaması gerçekleştiriyor. Kılavuz çizgilere snap etme, sınırlar ya da başka nesnelerle çarpışmayı engelleme, sürükleme işlemine tepki olarak boyutu veya konumu değişen animasyonlu bırakma hedefleri gibi kısıtları olan sürükleme materyalleri var mı merak ediyorum
    Eskiden, dairesel bir saati farklı uzunluklarda kama parçalarına bölerek odaklanma/dinlenme aralıklarını tanımlayan özel bir Pomodoro zamanlayıcı arayüzü yapmak istemiştim. Bu kama parçalarının sürüklenerek sırasının değiştirilmesini uygulamada pek ilerleme kaydedemedim

    • Bu kod yaklaşımında, sürükleme hareketi bir durumu güncelliyor. Durumu ayarlarken kısıtlar uygulanabilir ve bu durum da tekrar görüntüyü yönlendirir
      Kısıt sistemini olay işleme sisteminden ayırmak istiyordum. jquery-ui gibi kütüphaneler ikisini birbirine bağlıyor; bu yüzden olay işleme sisteminin olası tüm kısıtları bilmesi gerekiyor. jquery-ui sınırlayıcı kutu, eksen, kare ızgara ve DOM öğelerine snap etmeyi destekliyor. Ama altıgen ızgaraya, logaritmik ölçekli ızgaraya ya da dairesel sınıra snap etmek isterseniz desteklenmiyor
      Kodda state.pos = … ifadesini görebilirsiniz; durumun ayarlandığı yer orası. Kısıtlar için pos değerini bir setter’ın arkasına gizliyorum. Böylece sürükleme olayını işleyen kod hangi kısıtların gerektiğini bilmeden setter kısıtları uygulayabiliyor
      Sayfayı güncelleyip kısıt örnekleri göstermem gerekecek. Kod tarifinde bu yönden bahsetmeyi tamamen unutmuşum
      Eski bir kısıt örneği https://www.redblobgames.com/articles/curved-paths/making-of... adresinde, çarpışmayı önleme örneği ise https://redblobgames.github.io/circular-obstacle-pathfinding... adresinde var. Ancak sürükleyerek sıralamayı değiştirme ya da animasyonlu bırakma hedeflerini henüz denemedim
  • Bu siteye ben de minnettarım. Uber’de coğrafi analiz için altıgen sistem tasarlarken https://www.redblobgames.com/grids/hexagons/ kaynağından çok yararlandım

  • Düzgün etkileşimlerle uğraşırken ortaya çıkan tuzakları ve dikkat edilmesi gereken noktaları iyi özetleyen bir yazı
    Daha “hemen kullanılabilir” tarafta, birkaç projede interactjs’i epey uzun süre kullandım

  • Herkese teşekkürler. Bugün HN’de yer aldığını görünce şaşırdım; soruları yanıtlayabilirim

    • HTML’in yerleşik draggable="true" niteliğine ve drag events’e bakıp bakmadığınızı merak ediyorum
      Yerleşik özelliğin bu koda kıyasla eksik kaldığı noktalar var mı, onu da merak ediyorum
      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’te sürükleme üzerine harika bir tartışmaydı ve bilgiler gerçekten faydalıydı
      Kısa bir sorum var. dragstart gibi yerleşik DOM olaylarıyla sürükleme hareketini tek bir eksenle nasıl sınırlayabileceğimi merak ediyorum. dragstart/dragenter/dragover/drop olaylarıyla sürükle-bırak işlevini uyguladım, ancak hareketi x ekseni ile sınırlamanın hızlı bir yolunu bulamadım. JQuery’nin sürükle-bırak API’sinin bunu desteklediğini hatırlıyorum. Yalnızca yerel DOM olaylarını/API’lerini kullanmaya çalışıyorum; bu konuda bilgi ya da yönlendirme olursa iyi olur
    • Eskiden tarayıcı arayüzü kodu sürekli yazardım, ama birkaç yıl ara verdim. Birinin pointer events kullandığını ilk kez duyuyorum
      Safari’nin desteği 2020’de eklediği düşünüldüğünde, tarayıcıların bunu desteklememesinden endişe etmiyor musunuz merak ediyorum. macOS Mojave kullanıcıları da Safari 13 ya da 14 kullanıyor olmalı; umarım Safari 12 artık gerçekten kullanılmıyordur. Ama genel web kullanıcılarını hedefleyen bir production uygulamasında sürükleme kadar basit bir özelliğin çalışmaması oldukça kötü olurdu
      Sürükleme sırasında document üzerine olay işleyicisi eklemek eski bir pratik; tarayıcılar belirli kullanım durumlarını basitleştirmeye yönelik yeni özellikler eklemeyi sürdürüyor, ama her birinin kendi istisnaları ve tuzakları var. Yazıda da bunun henüz tamamen ele alınmadığı söyleniyor. Nihai sonuçta da pointer event’ler ile touch event’lerin bir kombinasyonu kalıyor
      Kodun azalması anlamındaki “basitliğin”; ek istisnalar, daha düşük tarayıcı desteği ve geliştiricilerin mouse event’lere kıyasla muhtemelen daha az anlaşılan ve belgelenen pointer event ayrıntılarını ve tarayıcı farklarını öğrenmek zorunda kalması pahasına değip değmediğini merak ediyorum
  • Eksik olan bir şey var: erişilebilirlik. Klavye kullanımıyla sürükleme nasıl mümkün kılınmalı?

    • Bence burada soru biraz konudan sapıyor. Bir çizim programında klavye kullanan birinin tuvale pan yapmak için tıklayıp basılı tutma eylemini nasıl yapacağını sormaya benziyor. Öyle yapılmaz. Fareyle pan yapmak veya nesneleri sürüklemek yalnızca bir arayüzdür; asıl görev tuvali pan yapmaktır ve bunu başarmanın çeşitli yolları vardır
      Benim örneğimde erişilebilirlik yanıtı, bu işi tamamlamak için ek klavye kontrolleri sunmaktır. Mümkünse bir tuşa sürekli basılı tutmayı gerektirmeyen bir yol daha iyi olur. Örneğin bir kısayolla pan moduna girip, ok tuşlarıyla görünüm alanını tuval üzerinde hareket ettirmek çözüm olur
      Sürüklenebilir nesneler söz konusu olduğunda görev sıralamayı değiştirmektir. Bunu erişilebilir yapmak için sıralamayı değiştirmenin alternatif bir yolunu sunmak yeterlidir. Örneğin TAB ile nesneler arasında odağı dolaştırmak, bir tuşla odaktaki nesneyi seçmek, ardından ok tuşlarıyla sürükleme önizlemesini o yöndeki en yakın geçerli konuma taşımak gibi
    • Genel olarak yanıt “öyle yapılmaz”a daha yakın. UI, işaretçi veya dokunmatik cihaz kullanamayan kişilerin de sayfayla etkileşime girebilmesi için sürüklemeye alternatif sunmalıdır
      Yani sürükleme uygulamasının içine klavye desteğini zorla sıkıştırmak yerine, sürükleme işlevinden ayrı olarak anlamlı bir klavye işlevi eklemek gerekir
    • Bu, taşınabilir bir nesne olurdu ve bu uygulamayı temel alarak kolayca yapılabilecek gibi görünüyor
    • TAB / Space / Enter ile öğeyi seçip, Ctrl + ok tuşlarıyla “sürüklenmekte olan” öğeyi hareket ettirmek yeterli. Elbette her tuşa basışta yalnızca bir blok hareket edebilir
  • Güzel. Bu haftanın başında bir Vue uygulamasında SVG öğesi sürükleme uyguluyordum; yazarın anlattığı neredeyse her şeyi aynı sırayla kendim keşfetmek zorunda kaldım ve sonuçta neredeyse aynı bileşene ulaştım
    Ancak ben bunu fonksiyonel bileşen yerine useDragging adlı bir composable yardımcı olarak yazdım

    • Harika. Directive ve slot içeren bileşeni denedim ama composable’ı henüz denemedim. Çünkü o yaklaşımla olay işleyicilerini nasıl ayarlamam gerektiğinden emin değildim. Composable konusunda çok deneyimli değilim