JavaScript 드래그 앤 드롭 효과 code_javascript 기술

WBOY
풀어 주다: 2016-05-16 18:57:40
원래의
969명이 탐색했습니다.

이 프로그램의 프로토타입은 제가 이미지 자르기 효과를 할 때 만들어졌는데, 당시 muxrwc와 BlueDestiny에서 비슷한 효과들을 몇 가지 참고하고 많이 배웠습니다.
정리할 때마다 기분이 좋지만, 가끔씩 배운 지식처럼 개선할 수 있는 부분도 있고, 오류도 있는 부분이 있고, 실현해야 할 특정 요구 사항이 있다는 걸 발견하게 됩니다.
간단한 드래그 앤 드롭만 필요한 사람들이 있다는 점을 고려하여 쉽게 학습할 수 있도록 SimpleDrag의 단순화된 버전이 있습니다.
효과 미리보기
ps: maxthon에서 광고 필터링을 켜면 필터링될 가능성이 높습니다(피할 수 있는 방법이 있는지는 모르겠습니다).
프로그램 설명
[프로그램 원리]
여기에서는 SimpleDrag를 예로 들어 기본 원리를 설명합니다.
먼저 초기화 프로그램에 드래그 앤 드롭 개체가 필요합니다.

코드 복사 코드는 다음과 같습니다.

this.Drag = $(drag);

처음에 드래그 앤 드롭 개체를 기준으로 마우스 좌표를 기록하는 두 가지 매개 변수도 있습니다.
코드 복사 코드는 다음과 같습니다.

this._x = this._y = 0 ;

제거 이벤트를 추가하는 데 사용되는 두 가지 이벤트 개체 함수도 있습니다.
코드 복사 코드는 다음과 같습니다.

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

은 각각 끌기 프로그램과 끌기 중지 프로그램입니다.
드래그 앤 드롭 개체의 위치는 절대 위치여야 합니다.
코드 복사 코드는 다음과 같습니다.

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

마지막으로 드래그 앤 드롭 시작 프로그램을 드래그 앤 드롭 이벤트에 바인딩합니다. -drop object:
addEventHandler(this.Drag, "mousedown ", BindAsEventListener(this, this.Start))
드래그 앤 드롭 개체 위에서 마우스를 누르면 시작 프로그램이 트리거됩니다. . 주로 드래그를 준비하는 데 사용됩니다. 여기에는 드래그 앤 드롭 개체를 기준으로 한 마우스 좌표가 기록됩니다.
코드 복사 코드는 다음과 같습니다.

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

그리고 _fM 끌기 프로그램과 _fS 중지 끌기 프로그램을 각각 문서의 mousemove 및 mouseup 이벤트에 바인딩합니다.
코드 복사 코드는 다음과 같습니다.

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

이벤트가 전체 창 문서에서 유효한지 확인하려면 이벤트를 문서에 바인딩해야 합니다. 드래그 앤 드롭 개체에만 바인딩하면 너무 빨리 드래그하여 터치에서 벗어나기 쉽습니다. .
문서 위에서 마우스를 움직이면 이동 프로그램이 실행됩니다. 드래그를 구현하는 프로그램은 다음과 같습니다.
드래그 앤 드롭 객체에 설정해야 하는 왼쪽 및 위쪽 값은 마우스의 현재 좌표값과 드래그 시작 시 마우스의 상대 좌표값의 차이로 얻을 수 있습니다.
이것 .Drag.style.left = oEvent.clientX - this._x "px";
this.Drag.style.top = oEvent.clientY - this._y "px"
드디어 마우스를 놓으면 드래그 앤 드롭을 종료하기 위해 중지 프로그램이 트리거됩니다.
여기서 주요 기능은 시작 프로그램에서 문서에 추가된 이벤트를 제거하는 것입니다.
removeEventHandler(document, "mousemove", this._fM)
removeEventHandler(document, "mouseup", this. _fS );
이런 간단한 드래그 앤 드롭 프로그램이 준비되어 있습니다.
【드래그 앤 드롭 잠금】
잠금에는 가로 잠금(LockX), 세로 잠금(LockY), 완전 잠금(Lock)의 세 가지 유형이 있습니다.
이건 비교적 간단합니다. 가로,세로 방향을 잠그려면 Move에서 잠겼는지 확인하고 왼쪽과 위쪽으로 설정하면 됩니다. 완전히 잠겼으면 바로 돌아가면 됩니다.
if(!this.LockX){ this.Drag.style.left = ; }
if(!this.LockY){ this.Drag.style.top = }
[트리거 개체]
트리거 개체는 프로그램의 Handle 속성을 통해 설정된 드래그 앤 드롭 프로그램을 트리거하는 데 사용됩니다. 트리거링에 드래그 앤 드롭 개체 전체를 사용할 필요가 없는 경우도 있습니다.
트리거 개체를 사용한 후에도 드래그 앤 드롭 개체는 계속 이동하지만 트리거 개체는 드래그 앤 드롭을 트리거하는 데 사용됩니다(일반적인 용도는 트리거 개체를 드래그 앤 드롭 개체에 넣는 것입니다).
ps: 트리거 객체의 또 다른 사용법은 동일한 핸들을 설정하여 하나의 트리거 객체로 여러 드래그 앤 드롭 객체를 동시에 드래그 앤 드롭하는 것입니다.
【범위 제한】
범위 제한을 설정하려면 먼저 제한을 true로 설정해야 합니다. 범위 제한에는 고정 범위 제한과 컨테이너 범위 제한 두 가지가 있으며 주로 Move 프로그램에서 설정합니다.
비교값이 범위를 초과할 경우 드래그 앤 드롭 개체가 설정 범위 내에 남을 수 있도록 왼쪽과 위쪽에 설정할 값을 수정하는 것이 원칙입니다.
[고정 범위 제한]
컨테이너 범위 제한은 위쪽, 아래쪽, 왼쪽, 오른쪽의 드래그 앤 드롭 범위를 지정하는 것입니다.
각 속성의 의미는 다음과 같습니다.
상단(mxTop): 상단 제한
하단(mxBottom): 상단 오프셋 높이 제한
왼쪽(mxLeft): 왼쪽 제한; ) : 왼쪽 offsetWidth 제한.
범위 설정이 올바르지 않으면 상하 또는 좌우가 동시에 범위를 초과할 수 있습니다. 프로그램에는 범위 매개변수를 수정하는 수리 프로그램이 있습니다.
복구 프로그램은 프로그램 초기화 및 시작 프로그램에서 실행됩니다. 복구 프로그램에서 mxRight 및 mxBottom을 수정합니다.
this.mxRight = Math.max(this.mxRight, this.mxLeft this.Drag.offsetWidth) ;
this.mxBottom = Math.max(this.mxBottom, this.mxTop this.Drag.offsetHeight)
여기서 mxLeft offsetWidth 및 mxTop offsetHeight는 각각 mxRight 및 mxBottom의 최소 범위 값입니다.
범위 매개변수에 따라 이동 매개변수를 수정합니다.
iLeft = Math.max(Math.min(iLeft, mxRight - this.Drag.offsetWidth), mxLeft)
iTop = Math.max(Math .min( iTop, mxBottom - this.Drag.offsetHeight), mxTop)
왼쪽 상단에는 더 큰 값을, 오른쪽 하단에는 더 작은 값을 취합니다.
【컨테이너 범위 제한】
컨테이너 범위 제한은 범위를 하나의 컨테이너_mxContainer로 제한하는 것을 의미합니다.
드래그 앤 드롭 개체는 _mxContainer에 포함되어야 합니다. 프로그램이 컨테이너 범위 제한을 설정하기 위해 상대 위치 지정을 사용하기 때문입니다(컨테이너 외부에 있는 경우 절대 위치 지정을 사용해야 하므로 더 번거롭습니다). ) 그리고 드래그 앤 드롭 개체보다 컨테이너 공간이 크기 때문에 이에 대해서는 설명할 필요가 없습니다.
범위 매개변수가 컨테이너의 속성에 따라 설정된다는 점을 제외하면 원리는 고정 범위 제한과 유사합니다.
컨테이너가 설정되면 복구 프로그램은 상대 위치 지정을 위해 위치를 자동으로 설정합니다.
!this._mxContainer || CurrentStyle(this._mxContainer).position == "relative" || .style.position = "relative");
ps: CurrentStyle은 최종 스타일을 얻는 데 사용됩니다(자세한 내용은 여기에서 최종 스타일 섹션을 참조하세요).
프로그램 실행 전에 드래그 앤 드롭 개체의 왼쪽과 위쪽을 설정했지만 컨테이너를 상대로 설정하지 않은 경우 상대가 자동으로 설정될 때 이동이 발생하므로 프로그램은 복구를 실행합니다. 이런 상황을 방지하려면 초기화 중에 프로그램을 실행하세요. 정확한 값을 얻기 위해서는 상대값을 설정하기 전에 offsetLeft와 offsetTop을 얻어야 하므로 시작 프로그램에서 _x, _y를 설정하기 전에 Repair를 수행해야 합니다.
상대 위치 지정으로 인해 컨테이너 범위의 경우 범위 매개변수의 위쪽, 아래쪽, 왼쪽 및 오른쪽 값은 각각 0, clientHeight, 0, clientWidth입니다.
clientWidth 및 clientHeight는 컨테이너에서 표시되는 부분의 너비와 높이입니다(자세한 내용은 여기를 참조하세요).
为了容器范围能兼容固定范围的参数,程序中会获取容器范围和固定范围中范围更小的值:
Code
mxLeft = Math.max(mxLeft, 0);
mxTop = Math.max(mxTop, 0);
mxRight = Math.min(mxRight, this._mxContainer.clientWidth);
mxBottom = Math.min(mxBottom, this._mxContainer.clientHeight);
因为设置相对定位的关系,容器_mxContainer设置过后一般不要取消或修改,否则很容易造成移位异常。
【鼠标捕获】
我在一个拖放实例中看到,即使鼠标移动到浏览器外面,拖放程序依然能够执行,仔细查看后发现是用了setCapture。
鼠标捕获(setCapture)是这个程序的重点,作用是将鼠标事件捕获到当前文档的指定的对象。这个对象会为当前应用程序或整个系统接收所有鼠标事件。
使用很简单:
this._Handle.setCapture();
setCapture捕获以下鼠标事件:onmousedown、onmouseup、onmousemove、onclick、ondblclick、onmouseover和onmouseout。
程序中主要是要捕获onmousemove和onmouseup事件。
msdn的介绍中还说到setCapture有一个bool参数,用来设置在容器内的鼠标事件是否都被容器捕获。
容器就是指调用setCapture的对象,大概意思就是:
参数为true时(默认)容器会捕获容器内所有对象的鼠标事件,即容器内的对象不会触发鼠标事件(跟容器外的对象一样);
参数为false时容器不会捕获容器内对象的鼠标事件,即容器内的对象可以正常地触发事件和取消冒泡。
而对于容器外的鼠标事件无论参数是什么都会被捕获,可以用下面这个简单的例子测试一下(ie):
Code


mouseover

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


这里的参数是true,一开始body会捕获所有鼠标事件,即使鼠标经过div也不会触发onmousemove事件。
换成false的话,div就可以捕获鼠标事件,就能触发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:
Code



<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:
Code



<script> <br>document.onmousemove=function(){aa.innerHTML =1} <br></script>
🎜>안타깝게도 권위 있는 정보가 없으면 추측만 할 수 밖에 없습니다. 아직 이해하지 못하는 부분이 많으므로 나중에 연구해 보겠습니다.
ff2 아래의 마우스 캡처에 버그가 있습니다. 드래그 앤 드롭 개체 내부에 텍스트 내용이 없고 브라우저 외부로 드래그 앤 드롭하면 캡처가 실패합니다.
와 같은 빈 텍스트를 드래그 앤 드롭 개체에 삽입하세요. 이 문제는 해결될 수 있지만 이 버그는 ff3에서 수정되었습니다.
[포커스 상실]
일반적인 상황에서는 마우스 캡쳐로 정상적으로 이벤트 캡쳐가 가능하지만, 브라우저 창의 포커스가 상실되면 캡쳐가 실패하게 됩니다.
포커스 손실을 일으킬 수 있는 작업에는 창 전환(Alt 탭 포함), 경고 및 팝업 및 기타 팝업 양식이 포함된다는 사실을 임시 테스트했습니다.
포커스가 손실되면 Stop 프로그램을 동시에 실행하여 드래그 앤 드롭을 종료해야 합니다. 그러나 포커스가 손실되면 mouseup 이벤트를 캡처할 수 없습니다. 즉, _fS가 트리거될 수 없습니다.
다행히 IE에는 캡처가 실패할 때 실행되는 onlosecapture 이벤트가 있습니다.
addEventHandler(this._Handle, "losecapture", this._fS); >그리고 이를 Stop 프로그램으로 이동합니다.
removeEventHandler(this._Handle, "losecapture", this._fS)
하지만 ff에는 유사한 메서드가 없지만 muxrwc는 대체하는 window.onblur 이벤트를 찾습니다. losscapture, 시작 프로그램에서 설정할 수 있습니다:
addEventHandler(window, "blur", this._fS)
중지 프로그램에서 제거:
removeEventHandler(window, "blur", this. _fS);
즉, Onblur 이벤트도 있습니다. losscapture 대신 window.onblur를 사용하면 코드 조각을 저장할 수 있습니다.
그런 다음 몇 가지 테스트를 수행한 결과 기본적으로 losscapture를 트리거하는 모든 상황이 동시에 window.onblur를 트리거한다는 사실을 발견했습니다. 이는 작동하는 것 같습니다.
그래서 Losscapture 대신 window.onblur를 사용하도록 프로그램을 수정했는데, 테스트 후 문제가 발생했습니다. Alt 탭을 사용하여 다른 창으로 전환하면 드래그를 계속할 수는 있지만 포커스가 사라졌어야 했습니다. 이번에는.
그래서 테스트 코드와 프로그램 코드를 하나씩 제거해 보니 DTD를 사용하면 다시 포커스를 얻을 때까지 window.onblur가 실행되지 않는다는 사실을 발견했습니다.
다음 코드를 사용하여 테스트할 수 있습니다.

<script>window.onblur=function(){alert(1)}</script>
다른 프로그램으로 전환한 후 다시 전환하면 됩니다. window.onblur를 실행하면 언급하지 않을 몇 가지 이상한 상황이 있습니다. 어쨌든 IE에서 window.onblur를 사용하는 것은 이상적이지 않습니다.
[기본 동작 취소]
선택한 상태의 텍스트 콘텐츠, 연결, 이미지에 대한 드래그 앤 드롭 작업은 시스템의 기본 동작을 트리거합니다. 예를 들어 IE에서 이미지 위에 마우스를 드래그하면 금지된 작업 상태로 인해 이 끌어서 놓기 절차를 실행하지 못했습니다.
그러나 IE에서 setCapture가 설정된 후에는 사용자 인터페이스를 통한 마우스 드래그 앤 드롭 작업 및 콘텐츠 선택이 금지됩니다.
setCapture 이후에는 문서 내용을 끌어서 놓을 수 없다는 뜻입니다. 여기서 끌어서 놓는 것은 시스템의 기본 동작을 의미합니다. 예를 들어 ondragstart는 실행되지 않습니다.
그러나 setCapture 매개변수가 false인 경우 컨테이너의 개체는 여전히 이벤트를 트리거할 수 있으므로(자세한 내용은 마우스 캡처 섹션 참조) setCapture 매개변수를 true로 설정하거나 기본값을 유지해야 합니다.
ff의 마우스 캡처에는 이 기능이 없지만, PreventDefault를 사용하여 이벤트의 기본 동작을 취소하면 해결할 수 있습니다.
oEvent.preventDefault()
ps: 사용한다고 합니다. PreventDefault를 사용하면 마우스업 손실이 발생하지만 ff3의 테스트에서는 이를 발견하지 못했습니다. 마우스업 손실이 발견되면 꼭 알려주세요.
【선택 지우기】
즉, setCapture 설정 후 콘텐츠 선택이 금지되지만, 설정 전에 선택한 콘텐츠는 삭제되지 않으며, 설정 후 다른 방법으로도 콘텐츠를 선택할 수 있습니다.
예를 들어 ctrl a를 사용하여 콘텐츠를 선택하세요.
ps: onkeydown, onkeyup 및 onkeypress 이벤트는 마우스 캡처의 영향을 받지 않습니다.
그리고 ff는 마우스를 누를 때 원래 선택한 콘텐츠를 지울 수 있지만 마우스를 드래그하고 Ctrl A를 누르면 계속 콘텐츠가 선택됩니다.
그러나 시스템 기본 작업을 취소한 후에는 이 선택이 드래그 앤 드롭 작업에 영향을 미치지 않습니다. 여기서 설정은 주로 더 나은 경험을 위한 것입니다.
과거에는 목표를 달성하기 위해 드래그 앤 드롭 개체 선택을 비활성화하는 방법, 즉 IE에서 드래그 앤 드롭 개체의 onselectstart를 false로 설정하고 스타일 MozUserSelect(css)를 설정하는 방법을 사용했습니다. :-moz-user-select)를 FF에서는 없음으로 설정합니다.
但这种方法只能禁止拖放对象本身被选择,后来找到个更好的方法清除选择,不但不影响拖放对象的选择效果,还能对整个文档进行清除:
ie:document.selection.empty()
ff:window.getSelection().removeAllRanges()
为了防止在拖放过程中选择内容,所以把它放到Move程序中,下面是兼容的写法:
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
【margin】
还有一个情况,当拖放对象设置了margin,那么拖放的时候就会错位(给SimpleDrag的拖放对象设置margin就可以测试)。
原因是在Start程序设置_x和_y时是使用offset获取的,而这个值是包括margin的,所以在设置left和top之前要减去这个margin。
但如果在Start程序中就去掉margin那么在Move程序中设置范围限制时就会计算错误,
所以最好是在Start程序中获取值:
this._marginLeft = parseInt(CurrentStyle(this.Drag).marginLeft) || 0;
this._marginTop = parseInt(CurrentStyle(this.Drag).marginTop) || 0;
在Move程序中设置值:
this.Drag.style.left = iLeft - this._marginLeft + "px";
this.Drag.style.top = iTop - this._marginTop + "px";
要注意margin要在范围修正之后再设置,否则会错位。
【透明背景bug】
在ie有一个透明背景bug(不知算不算bug),可以用下面的代码测试:
Code






点击div的背景会触发不了事件(点击边框或div里面的对象是可以触发的)。
到底什么时候会出现这个bug呢,再用下面的代码测试:
Code











测试代码中我把背景颜色(包括body)设成灰色,首先可以看出在蓝色div(测试对象)内只要触发点是在灰色上面,就能触发事件;相反,在其他不是背景的地方,即使是边框、图片,也不能触发事件。
就像是把灰色的背景的补到蓝色div上来,而且仅仅是背景能这样,多奇怪的设定啊。
这里要说明的是body比较特别,不管背景是不是透明,只要触发点是直接在body上就能触发事件。
我的结论是如果触发事件的对象背景是透明的,而事件的触发点不在对象内的元素上,也不是直接在body上,而且透明背景外没有非透明背景的话,那么事件触发就会失败。
这个结论写得不太好,因为我都不知改怎么表达这奇怪的设定,希望各位能明白。
ps:这里设置图片背景跟颜色背景效果是一样的。
那最好解决的方法就是给对象设一个非透明背景,但有时需求正好是要透明的,那怎么办呢?
首先想到的是加上背景色后设置完全透明,但这样连边框,容器内的对象等都完全透明了,不够好。
如果能保证触发点直接在body或非背景上也可以,如果这个也不能保证呢?
我想到的一个解决方法是在容器里面加一个层,覆盖整个容器,并设置背景色和完全透明:
Code
with(this._Handle.appendChild(document.createElement("div")).style){
width = height = "100%"; backgroundColor = "#fff"; filter = "alpha(opacity:0)";
}
到这里又不得不说ie6的一个渲染bug,用下面的代码测试(ie6):
Code




<script>setTimeout("aa.style.height=200",0)</script>
ie7 결과적으로 내부 div의 높이를 100%로 설정했음에도 불구하고 외부 div의 높이를 수정한 후에는 어떤 이유로 채워지지 않는 것을 알 수 있다.
이 div가 드래그 개체인데 갑자기 절반을 드래그할 수 없다면 작동하지 않습니다.
다행히 BlueDestiny에서 개체의 오버플로를 숨김으로 설정하면 내부의 div가 자동으로 다시 채워지는 방법을 알려주었습니다.
BlueDestiny는 "이 문제의 원인은 IE6의 렌더링 문제 때문입니다. 특정 CSS 속성을 통해 DOM을 변경하고 다시 렌더링할 수 있습니다."
이 버그는 어쨌든 Zoom으로 해결될 수 있습니다. DOM을 다시 렌더링하는 것입니다.
이 버그가 프로그램에 나타나는 경우 프로그램 선택적 매개변수 Transparent를 true로 설정하면 해당 레이어가 자동으로 삽입됩니다.
여기에 약간의 지식을 추가해 보면 위의 테스트 코드에서 html의 배경색을 설정했다는 것을 알 수 있습니다.
이 배경색을 제거하면 배경색이 페이지 전체에 설정된다는 것을 알 수 있습니다. 비록 본문은 항상 페이지의 배경색을 설정하는 데 사용되었지만 이제는 의심스러울 것입니다. 본문은 빨간색 상자의 일부인데 왜 설정합니까? 배경색은 전체 페이지에 적용할 수 있으며 html의 배경색을 설정하면 "정상적으로" 표시됩니다.
그 이유는 CSS21의 w3c 표준의 배경 섹션에서 확인할 수 있습니다.
단, HTML 문서의 경우 HTML 문서의 경우 BODY 요소에 배경을 지정하는 것이 좋습니다. 루트 HTML 요소가 '배경색'에 대해 '투명' 값을, '배경 이미지'에 대해 '없음' 값을 계산한 경우, 사용자 에이전트는 다음과 같은 경우 해당 HTML 요소의 첫 번째 BODY 요소 하위에서 해당 속성의 계산된 값을 대신 사용해야 합니다. 캔버스에 대한 배경을 그리며 해당 BODY 요소에 대한 배경을 칠해서는 안 됩니다. 이러한 배경은 루트 요소에 대해서만 칠해진 경우와 동일한 지점에 고정되어야 합니다. 이는 XHTML 문서에는 적용되지 않습니다. 🎜>我英 짜증나니까 그냥 번역해 보세요.
HTML 문서의 경우 작성자(브라우저 제작자에게 해당)는 HTML 요소 대신 BODY 요소의 배경을 사용하는 것이 좋습니다. HTML 문서의 루트 요소(HTML 요소)의 '배경색'이 '투명'이고 '배경 이미지'가 '없음'(이 두 가지가 기본값임)인 경우 HTML 요소는 다음과 같습니다. 배경 설정 시 사용되는 첫 번째 BODY 하위 요소의 속성 값과 해당 요소의 배경은 더 이상 렌더링되지 않습니다.
마지막 두 문장은 이해하기 어렵지만 그 이유를 설명하기에는 충분합니다.
【iframe】
페이지에 iframe이 삽입되어 있는 경우 iframe에서 마우스 캡처에 문제가 발생하므로 주의하셔야 합니다.
예를 들어 드래그 앤 드롭 컨테이너 내의 iframe에서 빠르게 이동하여 드롭하거나 컨테이너 외부의 iframe으로 마우스를 드래그하면 어쨌든 마우스는 iframe에 있습니다(다른 항목은 없다는 점에 유의하세요). 요소를 분리하는 경우) 문제가 발생합니다:
우선 캡처가 유효하지 않습니다. 마우스가 iframe에 있으면 드래그할 수 없지만 창의 흐림은 물론 losscapture도 실행되지 않습니다.
둘째, IE에서는 iframe을 몇 번 더 문지르면 IE가 종료될 수 있습니다(알 수 없는 이유로).
제가 생각한 몇 가지 방법은 다음과 같습니다.
페이지의 iframe을 숨기는 것은 비교적 간단하지만, iframe의 일부 중요한 정보도 숨겨지거나 페이지 레이아웃이 어긋나게 되어 결과적으로 사용자 경험이 좋지 않습니다.
마우스를 iframe으로 이동한 후 드래그 앤 드롭을 취소하세요. 어렵지는 않지만 사용자 경험도 좋지 않습니다.
각 iframe은 매우 투명한 레이어로 덮여 있습니다. 번거롭습니다. 각 iframe의 위치와 크기를 계산해야 합니다.
전체 페이지를 덮는 투명 레이어를 사용하는 것이 좋습니다. 이 방법은 아래에 소개되어 있습니다.
LightBox 콘텐츠 표시 효과를 모방하기 위해 만든 오버레이를 여기에 적용할 수 있습니다.
var ol = new OverLay({ Opacity: 0 }); 그런 다음 ol을 추가하면 됩니다. onStart 및 onStop 이벤트의 Show() 및 ol.Close()를 사용하여 오버레이를 표시하고 숨길 수 있으므로 iframe에서 드래그 앤 드롭이 실행되지 않는 한 문제가 없습니다.
다른 더 좋은 방법이 있으면 조언 부탁드립니다.
당분간은 여기까지입니다. 약간의 드래그 앤 드롭으로 이렇게 많은 지식을 얻을 수 있을 거라고는 예상하지 못했습니다.
스크롤이나 기타 사항은 아직 고려하지 않았습니다. 나중에 필요한 경우 연구하겠습니다. 속성: 기본값 // 설명
Handle: "", // 트리거 개체 설정(설정되지 않은 경우 드래그 앤 드롭 개체 사용)
Limit: false, // 범위 제한 설정 여부(설정되지 않은 경우) 사실입니다. 다음 매개변수가 유용합니다. 음수일 수 있습니다.)
mxLeft:0,//왼쪽 제한
mxRight:9999,//오른쪽 제한
mxTop:0,//상한 제한
mxBottom:9999,//하한
mxContainer:"",//컨테이너 내 제한 지정
LockX:false,//가로 드래그 앤 드롭 잠금 여부
LockY:false,/ /세로 드래그 앤 드롭 잠금 여부
Lock:false, //잠금 여부
Transparent: false, //투명 여부
onStart:function(){},//시작 시 실행 move
onMove:function(){},//이동 시 실행
onStop:function(){}//이동 종료 시 실행
Drag 속성은 드래그 앤 드롭 개체이며 투명하고 핸들입니다. 초기화 후에는 mxContainer를 설정할 수 없습니다.

프로그램 코드
코드




코드 복사
코드는 다음과 같습니다.

var isIE = (document.all) ? 사실 : 거짓;
var $ = function (id) {
return "string" == typeof id ? document.getElementById(id) : id;
};
var Class = {
create: function() {
return function() { this.initialize.apply(this, 인수); }
}
}
var Extend = function(destination, source) {
for(소스의 var 속성) {
destination[property] = source[property];
}
}
var Bind = function(object, fun) {
return function() {
return fun.apply(object, 인수);
}
}
var BindAsEventListener = function(object, fun) {
return function(event) {
return fun.call(object, (event || window.event));
}
}
var CurrentStyle = function(element){
return element.currentStyle || document.defaultView.getCompulatedStyle(요소, null);
}
function addEventHandler(oTarget, sEventType, fnHandler) {
if (oTarget.addEventListener) {
oTarget.addEventListener(sEventType, fnHandler, false);
} else if (oTarget.attachEvent) {
oTarget.attachEvent("on" sEventType, fnHandler);
} else {
oTarget["on" sEventType] = fnHandler;
}
};
function RemoveEventHandler(oTarget, sEventType, fnHandler) {
if (oTarget.removeEventListener) {
oTarget.removeEventListener(sEventType, fnHandler, false);
} else if (oTarget.detachEvent) {
oTarget.detachEvent("on" sEventType, fnHandler);
} else {
oTarget["on" sEventType] = null;
}
};
//拖放程序
var Drag = Class.create();
Drag.prototype = {
//拖放对象
initialize: function(drag, options) {
this.Drag = $(drag);//拖放对象
this. _x = this._y = 0;//记录鼠标상对拖放对象的位置
this._marginLeft = this._marginTop = 0;//记录margin
//事件对象(사용于绑定移除事件)
this._fM = BindAsEventListener(this, this.Move);
this._fS = 바인딩(this, this.Stop);
this.SetOptions(옵션);
this.Limit = !!this.options.Limit;
this.mxLeft = parsInt(this.options.mxLeft);
this.mxRight = parsInt(this.options.mxRight);
this.mxTop = parsInt(this.options.mxTop);
this.mxBottom = parsInt(this.options.mxBottom);
this.LockX = !!this.options.LockX;
this.LockY = !!this.options.LockY;
this.Lock = !!this.options.Lock;
this.onStart = this.options.onStart;
this.onMove = this.options.onMove;
this.onStop = this.options.onStop;
this._Handle = $(this.options.Handle) || 이.드래그;
this._mxContainer = $(this.options.mxContainer) || 널;
this.Drag.style.position = "절대";
//透明
if(isIE && !!this.options.Transparent){
//填充拖放对象
with(this._Handle.appendChild(document.createElement("div") ).style){
너비 = 높이 = "100%"; 배경색 = "#fff"; 필터 = "알파(불투명도:0)";
}
}
//修正范围
this.Repair();
addEventHandler(this._Handle, "mousedown", BindAsEventListener(this, this.Start));
},
//设置默认属性
SetOptions: function(options) {
this.options = {//默认值
Handle: "",//设置触发对象(不设置则使用拖放对象)
한도: false,//是否设置范围限system(为true时下face参数有,可以是负数)
mxLeft: 0,//左边限system
mxRight: 9999, //右边限제
mxTop: 0,//上边限제
mxBottom: 9999,//下边限제
mxContainer: "",//指定限aze 容器内
LockX: false,//是否锁정수평방向拖放
LockY: false,//是否锁定垂直方向拖放
Lock: false,//是否锁정
Transparent: false,//是否透明
onStart: function( ){},//开始移动时执行
onMove: function(){},//移动时执行
onStop: function(){}//结束移动时执行
};
확장(this.options, 옵션 || {});
},
//准备拖动
시작: function(oEvent) {
if(this.Lock){ return; }
this.Repair();
//记录鼠标상처
this._x = oEvent.clientX - this.Drag.offsetLeft;
this._y = oEvent.clientY - this.Drag.offsetTop;
//记录margin
this._marginLeft =parseInt(CurrentStyle(this.Drag).marginLeft) || 0;
this._marginTop = parsInt(CurrentStyle(this.Drag).marginTop) || 0;
//mousemove时移动 mouseup时停止
addEventHandler(document, "mousemove", this._fM);
addEventHandler(document, "mouseup", this._fS);
if(isIE){
//焦点丢失
addEventHandler(this._Handle, "losecapture", this._fS);
//设置鼠标捕获
this._Handle.setCapture();
}else{
//焦点丢失
addEventHandler(window, "blur", this._fS);
//阻止默认动작곡
oEvent.preventDefault();
};
//附加程序
this.onStart();
},
//수정 범위
복구: function() {
if(this.Limit){
//수정 오류 범위 매개변수
this.mxRight = Math.max(this.mxRight , this.mxLeft this.Drag.offsetWidth);
this.mxBottom = Math.max(this.mxBottom, this.mxTop this.Drag.offsetHeight)
//컨테이너가 있는 경우 위치는 다음과 같습니다. 상대 위치로 설정하고 오프셋을 가져오기 전에
을 설정하세요!this._mxContainer || CurrentStyle(this._mxContainer).position == "relative" || ;
}
},
//드래그
이동: function(oEvent) {
//잠겨 있는지 확인
if(this.Lock){ this.Stop( ); return; } ;
//선택 취소
window.getSelection().removeAllRanges() : document.selection.empty()
var iLeft = oEvent.clientX - this._x, iTop = oEvent.clientY - this._y
//범위 제한 설정
if(this.Limit){
//범위 매개변수 설정
var mxLeft = this.mxLeft , mxRight = this.mxRight, mxTop = this.mxTop, mxBottom = this.mxBottom;
//컨테이너가 설정된 경우 범위 매개변수를 수정하세요.
if(!!this._mxContainer) {
mxLeft = Math.max(mxLeft, 0);
mxTop = Math.max(mxTop, 0);
mxRight = Math.min(mxRight, this._mxContainer.clientWidth); mxBottom = Math.min( mxBottom, this._mxContainer.clientHeight);
};
//이동 매개변수 수정
iLeft = Math.max(Math.min(iLeft, mxRight - this.Drag. offsetWidth), mxLeft);
iTop = Math.max(Math.min(iTop, mxBottom - this.Drag.offsetHeight), mxTop)
}
//위치 및 올바른 여백 설정
if(!this.LockX ){ this.Drag.style.left = iLeft - this._marginLeft "px" }
if(!this.LockY){ this.Drag.style.top = iTop - 이. _marginTop "px"; }
//추가 프로그램
this.onMove()
},
//끌기 중지
Stop: function() {
//이벤트 제거
removeEventHandler( document, "mousemove", this._fM);
removeEventHandler(document, "mouseup", this._fS)
if(isIE){
removeEventHandler(this._Handle, " losscapture", this._fS);
this._Handle.releaseCapture();
}else{
removeEventHandler(window, "blur", this._fS);
};
/ /추가 프로그램
this.onStop();
}
};


테스트 코드:
코드




< ;/div>


🎜>
>
>

드래그 앤 드롭 상태: 시작되지 않음< ;/span>
< ;script>
var drag = new Drag("idDrag", { mxContainer: "idContainer", Handle: "idHandle", Limit: true,
onStart: function(){ $("idShow").innerHTML = "드래그 앤 드롭 시작" },
onMove: function(){ $("idShow").innerHTML = "left:" this.Drag.offsetLeft ";top:" this.Drag.offsetTop; },
onStop: function(){ $("idShow").innerHTML = "드래그 앤 드롭 종료"; }
}); onclick = function(){
drag .Limit = true;
drag.mxLeft = drag.mxTop = 0;
drag.mxRight = drag.mxBottom = 9999; LockY = drag.Lock = false;
}
$("idLock").onclick = function(){ drag.Lock = true }
$("idLockX").onclick = function() { drag.LockX = true; }
$("idLockY").onclick = function(){ drag.LockY = true; }
$("idLimit").onclick = function(){ drag.mxRight = drag.mxBottom = 200;drag.Limit = true; }
$("idLimitOff").onclick = function(){ drag.Limit = false }



관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!