Drag’n’Drop je skvělé řešení rozhraní. Vezmeme-li něco a přetáhneme, je to jasný a jednoduchý způsob, jak dělat mnoho věcí, od kopírování a přesouvání dokumentů (jako ve správcích souborů) až po objednávání (vkládání položek do košíku).
V moderním HTML standardně existuje sekce o Drag and Drop se speciálními událostmi, jako je dragstart
, dragend
atd.
Tyto události nám umožňují podporovat speciální druhy drag’n’drop, jako je manipulace s přetažením souboru ze správce souborů OS a jeho přetažením do okna prohlížeče. Potom může JavaScript přistupovat k obsahu těchto souborů.
Ale nativní události přetažení mají také omezení. Nemůžeme například zabránit přetažení z určité oblasti. Také nemůžeme provést tažení pouze „vodorovně“ nebo „svisle“. A existuje mnoho dalších úkolů drag’n’drop, které pomocí nich nelze provést. Podpora mobilních zařízení pro tyto události je také velmi slabá.
Takže zde uvidíme, jak implementovat Drag’n’Drop pomocí událostí myši.
Algoritmus Drag’n’Drop
Základní algoritmus Drag’n’Drop vypadá takto:
- Zapnuto
mousedown
– připravit prvek na přesun, pokud potřeba (možná vytvořit jeho klon, přidat do něj třídu nebo cokoli jiného). - Poté jej
mousemove
přesuňte změnouleft/top
sposition:absolute
. - Zapnuto
mouseup
– provádět všechny akce související s dokončením drag’n ‚drop.
Toto jsou základní informace. Později uvidíme, jak na další funkce, jako je zvýraznění aktuálních základních prvků, když je přetahujeme.
Zde je implementace přetažení koule:
Pokud spustíme kód, můžeme si všimnout něčeho zvláštního. Na začátku drag’n’dropu se koule „rozvětvuje“: začneme přetahovat její „klon“.
Zde je příklad akce:
Zkuste přetáhnout myší myší a uvidíte takové chování.
Je to proto, že prohlížeč má vlastní podporu drag’n’drop pro obrázky a některé další prvky. Spouští se automaticky a je v konfliktu s naším.
Chcete-li jej deaktivovat:
Nyní bude vše v pořádku.
V akci:
Další důležitý aspekt – sledujeme mousemove
na document
, ne na ball
. Na první pohled se může zdát, že myš je vždy nad míčem a můžeme na ni umístit mousemove
.
Ale jak si pamatujeme, mousemove
se spouští často, ale ne pro každý pixel. Takže po rychlém pohybu může ukazatel vyskočit z koule někde uprostřed dokumentu (nebo dokonce i mimo okno).
Takže bychom měli poslouchat document
chytit to.
Správné umístění
Ve výše uvedených příkladech se míč vždy pohybuje tak, aby jeho střed byl pod ukazatelem:
To není špatné, ale má to vedlejší efekt. Abychom zahájili drag’n’drop, můžeme mousedown
kdekoli na míči. Pokud to ale „vezmeme“ z jeho okraje, pak koule najednou „skočí“, aby se vystředila pod ukazatel myši.
Bylo by lepší, kdybychom ponechali počáteční posun prvku vzhledem k ukazateli.
Pokud například začneme táhnout za okraj koule, ukazatel by měl během tažení zůstat přes okraj.
Aktualizujme náš algoritmus:
Konečný kód s lepším umístěním:
V akci (uvnitř <iframe>
) :
Rozdíl je zvláště patrný, když míč přetáhneme za pravý dolní roh. V předchozím příkladu míč „vyskočí“ pod ukazatel. Nyní plynule sleduje ukazatel z aktuální polohy.
Potenciální cílové body (droppables)
V předchozích příkladech mohl míč být upuštěn „kdekoli“ a zůstat. V reálném životě obvykle vezmeme jeden prvek a hodíme ho na jiný. Například „soubor“ do „složky“ nebo něco jiného.
Když mluvíme abstraktně, vezmeme „přetahovatelný“ prvek a umístíme jej na „droppable“ prvek.
potřebujete vědět:
- kde byl prvek vypuštěn na konci Drag’n’Drop – provést odpovídající akci,
- a pokud možno znáte droppable přetahujeme se, abychom ho zvýraznili.
Řešení je trochu zajímavé a jen trochu ošidné, pojďme ho tedy popsat zde.
Co může být první nápad? Pravděpodobně nastavíme mouseover/mouseup
manipulátory na potenciální droppable?
Ale to nefunguje.
Problém je v tom, že když jsme přetažením je přetahovatelný prvek vždy nad ostatními prvky.Události myši se dějí pouze na horním prvku, nikoli na těch pod ním.
Níže jsou například dva prvky <div>
, červený nahoře na modrém jeden (zcela kryje). Neexistuje způsob, jak zachytit událost na modré, protože červená je nahoře:
Totéž s přetahovatelným prvkem. Míč je vždy nahoře nad ostatními prvky, takže na něm dochází k událostem. Bez ohledu na to, jaké obslužné rutiny nastavíme na nižší prvky, nebudou fungovat.
Proto prvotní myšlenka umístit obslužné rutiny na potenciální droppable v praxi nefunguje. Nebudou běžet.
Takže, co dělat?
Existuje metoda zvaná document.elementFromPoint(clientX, clientY)
. Vrátí nejvíce vnořený prvek na daných souřadnicích relativních k oknu (nebo null
, pokud jsou dané souřadnice mimo okno).
Můžeme jej použít v kterékoli z naše obslužné rutiny událostí myši, aby detekovaly potenciální droppable pod ukazatelem, například takto:
Poznámka: před voláním (*)
musíme míč skrýt. Jinak obvykle budeme mít na těchto souřadnicích kouli, protože jde o horní prvek pod ukazatelem: elemBelow=ball
. Takže to skryjeme a okamžitě znovu zobrazíme.
Pomocí tohoto kódu můžeme kdykoli zkontrolovat, nad kterým prvkem „letíme“. A zvládnout pokles, když k tomu dojde.
Rozšířený kód onMouseMove
k vyhledání prvků „droppable“:
V níže uvedeném příkladu, když je míč přetažen přes fotbalovou branku, je branka zvýrazněna.
Nyní máme v proměnné currentDroppable
během celého procesu aktuální „cílový cíl“, který letíme, a můžeme jej použít ke zvýraznění nebo jakékoli jiné věci.
Shrnutí
Zvažovali jsme základní algoritmus Drag’n’Drop.
Klíčové komponenty:
- Tok událostí:
ball.mousedown
→document.mousemove
→ball.mouseup
(nezapomeňte zrušit nativníondragstart
). - Na začátku přetažení – nezapomeňte na počáteční posun ukazatele vzhledem k prvku:
shiftX/shiftY
a ponechat to během přetahování. - Zjistit droppable prvky und e ukazatel pomocí
document.elementFromPoint
.
Na tomto základě můžeme hodně pokládat.
- Na
mouseup
můžeme intelektuálně dokončit pokles: měnit data, přesouvat prvky. - Můžeme zvýraznit prvky, nad kterými letíme.
- My může omezit přetahování o určitou oblast nebo směr.
- Pro
mousedown/up
můžeme použít delegování událostí. Obslužný program událostí pro velkou oblast, který kontrolujeevent.target
, může spravovat Drag’n’Drop pro stovky prvků. - A tak dále.
Existují rámce, které nad ní staví architekturu: DragZone
, Droppable
, Draggable
a další třídy. Většina z nich dělá podobné věci, jaké jsou popsány výše, takže by jim nyní mělo být snadné porozumět. Nebo si vytvořte vlastní, jak vidíte, že je to dost snadné, někdy jednodušší než přizpůsobení řešení třetí strany.