Heim  >  Artikel  >  Web-Frontend  >  Eingehende Untersuchung des Drag-and-Drop-Effekts von Javascript

Eingehende Untersuchung des Drag-and-Drop-Effekts von Javascript

黄舟
黄舟Original
2016-12-14 16:05:39814Durchsuche

Der Drag-and-Drop-Effekt, auch Drag-and-Drop genannt, ist einer der häufigsten js-Spezialeffekte.
Wenn man viele Details außer Acht lässt, ist die Umsetzung sehr einfach, aber oft sind es die Details, die die Schwierigkeit darstellen.
Der Prototyp dieses Programms wurde erstellt, als ich Bildschneideeffekte machte. Damals bezog ich mich auf mehrere ähnliche Effekte und lernte viel von muxrwc und BlueDestiny.
Obwohl ich mich jedes Mal gut fühle, wenn ich es in Ordnung bringe, werde ich hin und wieder feststellen, dass irgendwo etwas verbessert werden kann, irgendwo ein Fehler liegt und bestimmte Bedürfnisse verwirklicht werden müssen, genau wie das Wissen, das ich gelernt habe.

Da einige Leute möglicherweise nur einfaches Drag-and-Drop benötigen, gibt es eine vereinfachte Version von Drag-and-Drop SimpleDrag, um das Erlernen zu erleichtern.

Programmprinzip

Hier nehmen wir SimpleDrag als Beispiel, um die Grundprinzipien zu erklären.

Zuerst ist im Initialisierungsprogramm ein Drag-and-Drop-Objekt erforderlich:

this.Drag = $(drag);

Es gibt auch zwei Parameter, um das Relative aufzuzeichnen Ziehen und Ablegen der Maus am Anfang. Die Koordinaten des Objekts:

this._x = this._y = 0;

Es gibt auch zwei Ereignisobjektfunktionen zum Hinzufügen und Entfernen von Ereignissen :

this._fM = BindAsEventListener(this, this.Move);
this._fS = Bind(this, this.Stop);

sind das Ziehprogramm und das Stopp-Ziehen Programm bzw.
Die Position des Drag-and-Drop-Objekts muss eine absolute Positionierung sein:

this.Drag.style.position = "absolute";

Zuletzt binden Sie das Start-Drag-and-Objekt -Drop-Programm zum Drag-and-Drop-Objekt-Mousedown-Ereignis:

addEventHandler(this.Drag, "mousedown", BindAsEventListener(this, this.Start));

Das Startprogramm wird Wird ausgelöst, wenn die Maus auf das Drag-and-Drop-Objekt gedrückt wird. Wird hauptsächlich zur Vorbereitung des Ziehens verwendet. Hier werden die Koordinaten der Maus relativ zum Drag-and-Drop-Objekt aufgezeichnet:

this._x = oEvent. clientX - this.Drag.offsetLeft;
this._y = oEvent.clientY - this.Drag.offsetTop;

Und binden Sie das Drag-Programm _fM und das Stop-Drag-Programm _fS an die Mousemove- und Mouseup-Ereignisse des Dokuments bzw.:

addEventHandler(document, "mousemove" , this._fM);
addEventHandler(document, "mouseup", this._fS);

Binding to document kann sicherstellen, dass die Das Ereignis ist im gesamten Fensterdokument gültig.

Wenn sich die Maus über das Dokument bewegt, wird das Programm „Verschieben“ ausgelöst. Hier ist das Programm zum Implementieren des Ziehens.
Der linke und obere Wert, der für das Drag-and-Drop-Objekt festgelegt werden sollte, kann durch die Differenz zwischen dem aktuellen Koordinatenwert der Maus und dem relativen Koordinatenwert der Maus zu Beginn des Ziehens ermittelt werden:

dies .Drag.style.left = oEvent .clientX - this._x + "px";
this.Drag.style.top = oEvent.clientY - this._y + "px";

Nachdem endlich Durch Loslassen der Maus wird das Stop-Programm beendet.
Die Hauptfunktion hier besteht darin, die dem Dokument im Startprogramm hinzugefügten Ereignisse zu entfernen:

removeEventHandler(document, "mousemove", this._fM);
removeEventHandler(document, "mouseup"); , this._fS);

So ein einfaches Drag-and-Drop-Programm ist fertig.

Drag-and-Drop-Sperre

Es gibt drei Arten von Sperren, nämlich: horizontale Sperre (LockX), vertikale Sperre (LockY) und vollständige Sperre (Lock).
Um die horizontale und vertikale Richtung zu sperren, müssen Sie nur feststellen, ob es in „Verschieben“ gesperrt ist, und es dann nach links und oben einstellen. Wenn es vollständig gesperrt ist, kehren Sie einfach zurück.

if(!this.LockX){ this.Drag.style.left = ...; }
if(!this.LockY){ this.Drag.style.top = ...; }

Trigger-Objekt

Das Trigger-Objekt dient zum Auslösen des Drag-and-Drop-Programms. Manchmal muss nicht das gesamte Drag-and-Drop-Objekt zum Auslösen verwendet werden. In diesem Fall wird das Trigger-Objekt benötigt.
Nach der Verwendung des Trigger-Objekts wird das Drag-and-Drop-Objekt weiterhin verschoben, aber das Trigger-Objekt wird zum Auslösen des Drag-and-Drop verwendet (im Allgemeinen wird das Trigger-Objekt in das Drag-and-Drop-Objekt eingefügt).

Bereichsbegrenzung

Um eine Bereichsbegrenzung festzulegen, muss Limit zuerst auf true gesetzt werden. Es gibt zwei Arten von Bereichseinschränkungen, nämlich feste Bereichseinschränkungen und Containerbereichseinschränkungen, die hauptsächlich im Move-Programm festgelegt werden.
Das Prinzip besteht darin, dass, wenn der verglichene Wert den Bereich überschreitet, die einzustellenden Werte für links und oben korrigiert werden, sodass das Drag-and-Drop-Objekt innerhalb des eingestellten Bereichs bleiben kann.

Feste Bereichsbegrenzung

Die Bereichsbegrenzung des Containers besteht darin, den Drag-and-Drop-Bereich oben, unten, links und rechts anzugeben.
Die Bedeutung jedes Attributs ist:

Top (mxTop): obere Grenze; : linkes Limit;

right (mxRight): left+offsetWidth limit.

Wenn der Bereich falsch eingestellt ist, kann es dazu führen, dass der obere und untere oder linke und rechte Bereich gleichzeitig überschritten werden. Im Programm ist ein Reparaturprogramm zur Korrektur der Bereichsparameter enthalten.

Das Reparaturprogramm wird in der Programminitialisierung und im Startprogramm ausgeführt. Korrigieren Sie mxRight und mxBottom im Reparaturprogramm:

this.mxRight = Math.max(this.mxRight, this.mxLeft + this. Drag.offsetWidth);

this.mxBottom = Math.max(this.mxBottom, this.mxTop + this.Drag.offsetHeight);

wobei mxLeft+offsetWidth und mxTop+offsetHeight das Minimum von mxRight sind und mxBottom bzw. Bereichswert.


Korrigieren Sie den Bewegungsparameter entsprechend dem Bereichsparameter:

iLeft = Math.max(Math.min(iLeft, mxRight - this.Drag.offsetWidth), mxLeft); = Math. max(Math.min(iTop, mxBottom - this.Drag.offsetHeight), mxTop);

Nehmen Sie einen größeren Wert für die obere linke Seite und einen kleineren Wert für die untere rechte Seite.

容器范围限制

容器范围限制的意思就是把范围限制在一个容器_mxContainer内。 
要注意的是拖放对象必须包含在_mxContainer中,因为程序中是使用相对定位来设置容器范围限制的(如果是在容器外就要用绝对定位,这样处理就比较麻烦了),还有就是容器空间要比拖放对象大,这个就不用说明了吧。 
原理跟固定范围限制差不多,只是范围参数是根据容器的属性的设置的。

当设置了容器,会自动把position设为relative来相对定位:

!this._mxContainer || CurrentStyle(this._mxContainer).position == "relative" || (this._mxContainer.style.position = "relative");

注意relative要在获取offsetLeft和offsetTop即设置_x和_y之前设置,offset才能正确获取值。

由于是相对定位,对于容器范围来说范围参数上下左右的值分别是0、clientHeight、0、clientWidth。

clientWidth和clientHeight是容器可视部分的宽度和高度(详细参考这里)。 
为了容器范围能兼容固定范围的参数,程序中会获取容器范围和固定范围中范围更小的值:

mxLeft = Math.max(mxLeft, 0); 
mxTop = Math.max(mxTop, 0); 
mxRight = Math.min(mxRight, this._mxContainer.clientWidth); 
mxBottom = Math.min(mxBottom, this._mxContainer.clientHeight);

注意如果在程序执行之前设置过拖放对象的left和top而容器没有设置relative,在自动设置relative时会发生移位现象,所以程序在初始化时就执行一次Repair程序防止这种情况。因为offsetLeft和offsetTop要在设置relative之前获取才能正确获取值,所以在Start程序中Repair要在设置_x和_y之前执行。

因为设置相对定位的关系,容器_mxContainer设置过后一般不要取消或修改,否则很容易造成移位异常。

鼠标捕获

我在一个拖放实例中看到,即使鼠标移动到浏览器外面,拖放程序依然能够执行,仔细查看后发现是用了setCapture。 
鼠标捕获(setCapture)是这个程序的重点,作用是将鼠标事件捕获到当前文档的指定的对象。这个对象会为当前应用程序或整个系统接收所有鼠标事件。 
使用很简单:

this._Handle.setCapture();

setCapture捕获以下鼠标事件:onmousedown、onmouseup、onmousemove、onclick、ondblclick、onmouseover和onmouseout。 
程序中主要是要捕获onmousemove和onmouseup事件。 
msdn的介绍中还说到setCapture有一个bool参数,用来设置在容器内的鼠标事件是否都被容器捕获。
容器就是指调用setCapture的对象,大概意思就是: 
参数为true时(默认)容器会捕获容器内所有对象的鼠标事件,即容器内的对象不会触发鼠标事件(跟容器外的对象一样); 
参数为false时容器不会捕获容器内对象的鼠标事件,即容器内的对象可以正常地触发事件和取消冒泡。 
而对于容器外的鼠标事件无论参数是什么都会被捕获, 
可以用下面这个简单的例子测试一下(ie):

 
 

mouseover
 
<script>document.body.setCapture(); </script> 
 

这里的参数是true,一开始body会捕获所有鼠标事件,即使鼠标经过div也不会触发onmousemove事件。 
换成false的话,div就可以捕获鼠标事件,就能触发onmousemove事件了。

拖放结束后还要使用releaseCapture释放鼠标,这个可以放在Stop程序中:

this._Handle.releaseCapture();

setCapture是ie的鼠标捕获方法,对于ff也有对应的captureEvents和releaseEvents方法。 
但这两个方法只能由window来调用,而且muxrwc说这两个方法在DOM2里已经废弃了,在ff里已经没用了。 
不过ff里貌似会自动设置取消鼠标捕获,但具体的情形就不清楚了,找不到一个比较详细的介绍,谁有这方面的资料记得告诉我啊。

下面都是我的猜测,ff的鼠标捕获相当于能自动设置和释放的document.body.setCapture(false)。 
因为我测试下面的程序,发现ie和ff效果是差不多的: 
ie:

 
 

 
<script> <br>document.body.onmousedown=function(){this.setCapture(false)} <br>document.body.onmouseup=function(){this.releaseCapture()} <br>document.onmousemove=function(){aa.innerHTML+=1} <br></script> 
 

ff:

 
 


<script> <br>document.onmousemove=function(){aa.innerHTML+=1} <br></script>

Leider kann ich ohne verlässliche Informationsreferenz nur raten. Es gibt immer noch viele Dinge, die ich immer noch nicht verstehe, und ich werde sie später studieren.

Beachten Sie, dass es einen Fehler bei der Mauserfassung unter ff2 gibt. Wenn das Drag-and-Drop-Objekt keinen Textinhalt enthält und per Drag-and-Drop aus dem Browser gezogen wird, schlägt die Erfassung fehl.

Fügen Sie einen leeren Text in das Drag-and-Drop-Objekt ein, z. B.   Dies kann behoben werden, aber dieser Fehler wurde in ff3 behoben.

Fokus verloren

Unter normalen Umständen kann die Mauserfassung Ereignisse normal erfassen, aber wenn der Fokus des Browserfensters verloren geht, schlägt die Erfassung fehl.

Ich habe vorübergehend getestet, dass Vorgänge, die zu Fokusverlust führen können, das Wechseln von Fenstern (einschließlich Alt+Tab), Warnungen und Popups sowie andere Popup-Formulare umfassen.
Wenn der Fokus verloren geht, sollte gleichzeitig das Stop-Programm ausgeführt werden, um das Ziehen und Ablegen zu beenden. Wenn der Fokus jedoch verloren geht, kann das Mouseup-Ereignis nicht erfasst werden, d. h. _fS kann nicht ausgelöst werden.
Glücklicherweise verfügt der IE über ein onlosecapture-Ereignis, das ausgelöst wird, wenn die Erfassung fehlschlägt. Für diese Situation können Sie es wie folgt festlegen:

addEventHandler(this._Handle, "losecapture", this._fS) ;

Und entfernen Sie es im Stop-Programm:

removeEventHandler(this._Handle, "losecapture", this._fS);

Aber ff hat keine ähnliche Methode , aber muxrwc hat ein Fenster gefunden, das das Ereignis losecapture .onblur ersetzt. Dann können Sie es im Startprogramm festlegen:

addEventHandler(window, "blur", this._fS);

Entfernen im Stop-Programm:

removeEventHandler(window, "blur", this._fS);

Dann hat IE auch das window.onblur-Ereignis, sodass die Verwendung von window.onblur anstelle von losecapture ein sparen kann Stück Code.

Dann habe ich einige Tests durchgeführt und festgestellt, dass grundsätzlich jede Situation, die losecapture auslöst, gleichzeitig window.onblur auslöst. Es scheint zu funktionieren.
Also habe ich das Programm geändert, um window.onblur anstelle von losecapture zu verwenden, aber nach dem Testen ist etwas schiefgegangen. Ich habe festgestellt, dass das Ziehen fortgesetzt werden konnte, wenn ich Alt+Tab verwendet habe, aber der Fokus hätte verloren gehen sollen zu diesem Zeitpunkt.

Also habe ich den Test- und Programmcode nacheinander entfernt und festgestellt, dass bei Verwendung einer DTD window.onblur erst dann ausgelöst wird, wenn es wieder den Fokus erhält.

Sie können den folgenden Code zum Testen verwenden:

<script>window.onblur=function(){alert(1)} </script>

Nachher zu anderen Programmen wechseln dass window.onblur erst nach dem Zurückschalten ausgelöst wird. Es gibt ein paar seltsame Situationen, die ich nicht erwähnen werde. Die Verwendung von window.onblur im IE ist jedoch nicht ideal.

Standardaktion

Drag-and-Drop-Vorgänge an Textinhalten, Verbindungen und Bildern im ausgewählten Zustand lösen die Standardaktion des Systems aus, beispielsweise das Ziehen der Maus über ein Bild im IE Dies führt dazu, dass das Drag-and-Drop-Programm fehlschlägt.

Nachdem setCapture jedoch im IE festgelegt wurde, sind Drag-and-Drop-Vorgänge und die Auswahl von Inhalten mit der Maus über die Benutzeroberfläche verboten.

Das bedeutet, dass Sie den Dokumentinhalt nach setCapture nicht per Drag & Drop auswählen können. Beachten Sie, dass sich Drag & Drop hier auf die Standardaktion des Systems bezieht. Beispielsweise wird ondragstart nicht ausgelöst.
Wenn jedoch der Parameter von setCapture „false“ ist, kann das Objekt im Container das Ereignis immer noch auslösen (Einzelheiten finden Sie im Abschnitt „Mauserfassung“). Daher sollte der Parameter von „setCapture“ auf „true“ gesetzt werden oder den Standardwert beibehalten.

Die Mauserfassung von ff verfügt nicht über diese Funktion, sie kann jedoch gelöst werden, indem präventDefault verwendet wird, um die Standardaktion des Ereignisses abzubrechen:

oEvent.preventDefault();

ps: Es wird gesagt, dass die Verwendung von „preventDefault“ zu einem Mouseup-Verlust führt, aber ich habe es im Test in ff3 nicht gefunden. Wenn Sie einen Mouseup-Verlust feststellen, sagen Sie mir unbedingt

Löschen Sie die Auswahl

d. h. Inhalte werden nach dem Festlegen der SetCapture-Auswahl verboten, aber Inhalte, die vor dem Festlegen ausgewählt wurden, werden nicht gelöscht, und Inhalte können nach dem Festlegen auch mit anderen Methoden ausgewählt werden,

z. B. mit Strg+A zum Auswählen Inhalt.
ps: onkeydown-, onkeyup- und onkeypress-Ereignisse werden von der Mauserfassung nicht beeinflusst.
Und ff kann den ursprünglich ausgewählten Inhalt löschen, wenn die Maus gedrückt wird, aber durch Ziehen der Maus und Strg+A wird der Inhalt weiterhin ausgewählt.
Nachdem die Standardaktion des Systems verworfen wurde, hat diese Auswahl jedoch keinen Einfluss auf den Drag-and-Drop-Vorgang. Die Einstellung hier dient hauptsächlich dem besseren Erlebnis.

In der Vergangenheit habe ich die Methode zum Deaktivieren der Auswahl von Drag-and-Drop-Objekten verwendet, um das Ziel zu erreichen, d MozUserSelect (css:-moz-user-select) in FF als keine.

Diese Methode kann jedoch nur verhindern, dass das Drag-and-Drop-Objekt selbst ausgewählt wird. Später habe ich einen besseren Weg gefunden, die Auswahl aufzuheben, was sich nicht nur nicht auf den Auswahleffekt des Drag-and-Drop-Objekts auswirkt, sondern auch löscht das gesamte Dokument:

ie: document.selection.empty()

ff: window.getSelection().removeAllRanges()

Um zu verhindern, dass Inhalte während des Drag-and-Drop-Vorgangs ausgewählt werden, fügen Sie sie in das Move-Programm ein. Die folgende kompatible Schreibmethode ist:

window.getSelection ? window.getSelection(). .removeAllRanges() : document.selecty();

margin

Es gibt eine andere Situation: Wenn das Drag-and-Drop-Objekt mit einem Rand festgelegt ist, wird es beim Ziehen und Ablegen falsch ausgerichtet Dropping (das Festlegen des Randes für das SimpleDrag-Drag-and-Drop-Objekt kann getestet werden).
Der Grund dafür ist, dass beim Festlegen von _x und _y im Startprogramm der Offset verwendet wird und dieser Wert den Rand einschließt, sodass dieser Rand vor der Einstellung von links und oben subtrahiert werden muss.
Wenn der Rand jedoch im Startprogramm entfernt wird, tritt beim Festlegen der Bereichsgrenze im Verschieben-Programm ein Berechnungsfehler auf.
Daher ist es am besten, den Wert im Startprogramm abzurufen:

this._marginLeft = parseInt(CurrentStyle(this.Drag).marginLeft) ||. 0;
this._marginTop = parseInt(CurrentStyle(this.Drag).marginTop) ||. 0;

wobei CurrentStyle wird verwendet, um die endgültigen Stile zu erhalten. Weitere Informationen finden Sie im Abschnitt „Endgültige Stile“ hier.

Legen Sie den Wert im Move-Programm fest:

this.Drag.style.left = iLeft - this._marginLeft + "px";
this.Drag.style.top = iTop - this._marginTop + "px";

Bitte beachten Sie, dass der Rand nach der Bereichskorrektur festgelegt werden muss, da er sonst falsch ausgerichtet wird.

[Transparenter Hintergrundfehler]

Es gibt einen transparenten Hintergrundfehler im IE (ich weiß nicht, ob es ein Fehler ist). Sie können den folgenden Code verwenden, um ihn zu testen:



">




Sie werden feststellen, dass das Ereignis nicht durch Klicken auf den Hintergrund ausgelöst werden kann, aber dennoch möglich ist ausgelöst durch Klicken auf den Rand.

Warum? Verwenden Sie dann zum Testen den folgenden Code:



- color:#00f;margin:50px;">







Sie sollten in der Lage sein, eine ungefähre Vorstellung davon zu bekommen, wie folgt zwei Divs sind jenseits Der Körperteil (dh jenseits des roten Kästchens) kann das Ereignis nicht auslösen.
Das heißt, wenn der Punkt, der das Ereignis auslöst, außerhalb des Körpers liegt und der Hintergrund transparent ist, wird fälschlicherweise angenommen, dass sich der Auslösepunkt an einer leeren Stelle außerhalb des Körpers befindet, sodass das Ereignis nicht stattfinden kann ausgelöst.

Die Lösung besteht darin, den Ereignisauslöser im Körper zu belassen oder einen nicht transparenten Hintergrund festzulegen.


Im Programm müssen Sie nur eine Hintergrundfarbe für das Drag-and-Drop-Objekt festlegen. Manchmal ist jedoch Transparenz erforderlich (z. B. bei Schnitteffekten). Was sollen wir also tun?
Das erste, was mir in den Sinn kommt, ist, eine Hintergrundfarbe hinzuzufügen und sie vollständig transparent einzustellen, aber dann sind sogar der Rand und die Objekte im Container vollständig transparent, was nicht gut ist.

Eine Lösung, die ich mir überlegt habe, besteht darin, eine Ebene innerhalb des Containers hinzuzufügen, die den gesamten Container abdeckt und die Hintergrundfarbe und vollständige Transparenz festlegt:


with(this._Handle.appendChild(document.createElement(" div" )).style){
width = height = "100%"; backgroundColor = "#fff"; filter = "alpha(opacity:0)";

}


Wenn es Es wird festgestellt, dass das Programm Folgendes hat: Wenn dieser Fehler auftritt, wird durch Setzen des optionalen Programmparameters „Transparent“ auf „true“ automatisch eine solche Ebene eingefügt.
Wenn Sie eine bessere Methode haben, geben Sie mir bitte einen Rat.

Das war's vorerst. Es gibt jedoch noch nicht berücksichtigte Iframes, Scrolling usw. Wir werden sie später bei Bedarf untersuchen.

Weitere verwandte Artikel finden Sie auf der chinesischen PHP-Website (www.php.cn)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:Der Unterschied zwischen Javascript und JavaNächster Artikel:Der Unterschied zwischen Javascript und Java

In Verbindung stehende Artikel

Mehr sehen