数年前、私は不動産ウェブサイトのデザインと開発に参加し、フロントエンドの仕事を担当しました。プロジェクトマネージャーの要求は比較的高いため、不動産の優れた機能を数多く参照しました。他の人の優れたデザインやアイデアを集めようと思って集まったとき、その時点でのデザイン案や機能の実装は何度も変更されただけで、今日達成された良い効果は翌日に延期される可能性があります。それについては話さないでください。今日説明するケースについて話しましょう。Soufun.com にアクセスしたことがあるのかどうかはわかりません。Soufun は支払うことができますか。広告料?)、プロダクト マネージャーが特に気に入っている機能が 1 つあります。それは次のとおりです:
これは現在の効果ですが、おそらくいくつかの変更が加えられています。元の効果は、中の絵を上下左右にドラッグすると、家に表示されている建物番号も絵とともに移動します。 time, js 能力が十分ではなく、プロジェクトマネージャーの要求を達成できませんでしたが、後にプロジェクトマネージャーがこのエフェクトを拒否し、別のエフェクトに置き換えました
プロジェクトマネージャーはこのような影響を望んでいませんでしたが、その時は私の心に深い傷を残し、今でも忘れることができません。
今日このブログを書く私の本来の目的は、この種の抗力効果を達成したいが、それを達成する方法がわからない学生に、離脱しないようにするためのアイデアを提供することを願っています。もちろん、ドラッグ アンド ドロップを実装する方法はたくさんありますが、ここでは 1 つの方法だけを JavaScript で紹介し、ゆっくりと原理を理解していきます。
さて、冗談は終わりです。本題に取り掛かりましょう。まず、ドラッグとは何かを理解する必要があります。しかし、それでも説明しておきたいと思います。
ドラッグ アンド ドロップは、マウスを使用してページ上でドラッグできます。正確に説明すると、マウスをコンテナに移動してからマウスを放さないように注意してください。実際の例では、テーブルの上に箱があり、その上に手を置いて移動します。手が止まると箱が止まります、取ってください、箱は動きません、ふふ、分かりました!上記の内容はナンセンスであると思わないでください。そこから多くの情報を得ることができます。要約は次のとおりです:
ドラッグ = マウスダウン + マウス移動 + マウスアップ
これでドラッグ アンド ドロップのタスクが完了しました。これがドラッグ アンド ドロップの原理であることが分かりました。ドラッグ アンド ドロップの効果をシミュレートするために上記の 3 つのアクションを自然に実装できます。そうですね、これは JavaScript の構文に対応しています:の 3 つのアクションを実装するだけです。
onmousedown、onmousemove、onmouseup
実装されるコードは次のようになります:
obj.onmousedown = function(ev){ obj.onmousemove = function(ev){ } ; obj.onmouseup = function(ev){ }; }
まず、オブジェクトを移動するには左と上の値を操作する必要があるため、マウスの移動自体に距離を置く必要があります。マウスが移動したことを知っていますか? オブジェクトまでの距離を指定すると、オブジェクトはマウスと同じ距離を移動しますか?はは、ちょっとアイデアがあって、かわいい感じがします〜 ここで詳しく知りたい場合は、ボックス モデルを確認してください。多くのマスターも関連ブログを持っています。それを示すために次の写真を使用します。
説明: 青いボックスは画面の幅と高さ、太い黒いボックスはブラウザの表示領域の幅と高さ (ブラウザの縮小効果)、細い黒いボックスはマウスでドラッグされるオブジェクトです図に示すように、マウスの座標を取得します。event.clientX、event.clientY を使用して取得できます。
計算の一般原理は以下の図を参照してください:
説明: 左は初期位置、右はターゲット位置、原点はマウスの位置、大きな黒いボックスはブラウザの表示幅、小さな黒いボックスはドラッグ オブジェクト、ステータスを参照してください。オブジェクトを目的の位置にドラッグし、マウスの最終的な位置を取得し、マウスとオブジェクトの差を減算して、オブジェクトの上部と左の値に代入します。マウスの位置の差を取得し、その差を最初の上と左の値に追加することもできます。2 番目の種類も可能です。自分で試してみてください。
obj.onmousedown = function(ev){ var ev = ev || event; var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop; document.onmousemove = function(ev){ var ev = ev || event; obj.style.left = ev.clientX - disX + 'px'; obj.style.top = ev.clientY - disY + 'px'; }; document.onmouseup = function(ev){ var ev = ev || event; document.onmousemove = document.onmouseup = null; }; }
这里说明一下:onmousemove和onmouseup之所以用document对象而不用obj对象,是因为如果用obj对象,鼠标在obj内部还好,如果在obj外面的话,拖拽会很怪异,你也可以改成obj体会一下,最后我们在鼠标弹起的时候将事件都清空;
上面的基本拖拽就算完成了,但是细心的同学一定会问,如果页面上有文字的话,拖拽物体会将文字选中,这效果岂不是怪怪的,没错,这是因为拖拽的时候触发了浏览器的默认选择事件,所以,在拖拽的时候,我们要清除这个默认事件,那怎么清除呢?
下面给一个兼容性写法:
if(ev.stopPropagation){ ev.stopPropagation(); }else{ ev.cancelBubble = true; //兼容IE } //简写成 ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true;
将上面的代码放在onmousedown下,鼠标按下就清除浏览器默认事件,文字就不会被选中了,好了,一个简单的拖拽效果就完成了,当然你现在是看不到效果,之所以不给demo链接是为了让你自己试着写一写,这样印象更深刻,
好了,那问题又来了,到这里就这样完了吗?。。。。。。按本人的风格,当然没有,干货还在后面!
如果我想实现这样一个效果,就是这一个大的容器里面(可以是box,也可以是document),怎么样能让我们的拖拽对象不跑出去呢,换句话说,拖到边缘就拖不动了,耶,是不是很多人想要实现的效果,哈哈,我们看看实现的原理是什么:
现实生活中,一个物体在一个盒子里跑不出去,是因为有堵墙,那我们只要能模拟出这堵墙,就可以把物体框起来,那这堵墙要怎么做呢?我们可以换个思路,当拖拽对象拖到边缘的时候,比如说拖到右边,我们将它的left固定住,是不是就不能再往右了,因为left值不能再加了,那么拖到底部,同理我们将top值固定住,就不能再往下拖了,理解吗?
最终的结果就是如下:
//左侧 if(obj.offsetLeft <=0){ obj.style.left = 0; }; //右侧 if(obj.offsetLeft >= pWidth - oWidth){ obj.style.left = pWidth - oWidth + 'px'; }; //上面 if(obj.offsetTop <= 0){ obj.style.top = 0; }; //下面 if(obj.offsetTop >= pHeight - oHeight){ obj.style.top = pHeight - oHeight + 'px'; };
说明:pWidth,pHeight 表示父级元素的宽高(这里是表示相对于父级的宽高限制),oWidth,oHeigt表示拖拽元素的宽高
最后,我将整个拖拽代码整理了一下:
/* 参数说明: 元素绝对定位,父级相对定位,如果父级为window,则可以不用 传一个参数,表示父级为window,物体相对于window范围拖动 传2个参数,则父级为第二个参数,物体相对于父级范围拖动 参数为id值 */ function drag(obj,parentNode){ var obj = document.getElementById(obj); if(arguments.length == 1){ var parentNode = window.self; var pWidth = parentNode.innerWidth,pHeight = parentNode.innerHeight; }else{ var parentNode = document.getElementById(parentNode); var pWidth = parentNode.offsetWidth,pHeight = parentNode.offsetHeight; } obj.onmousedown = function(ev){ var ev = ev || event; var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop; var oWidth = obj.offsetWidth,oHeight = obj.offsetHeight; //阻止冒泡时间 ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true; document.onmousemove = function(ev){ var ev = ev || event; obj.style.left = ev.clientX - disX + 'px'; obj.style.top = ev.clientY - disY + 'px'; //左侧 if(obj.offsetLeft <=0){ obj.style.left = 0; }; //右侧 if(obj.offsetLeft >= pWidth - oWidth){ obj.style.left = pWidth - oWidth + 'px'; }; //上面 if(obj.offsetTop <= 0){ obj.style.top = 0; }; //下面 if(obj.offsetTop >= pHeight - oHeight){ obj.style.top = pHeight - oHeight + 'px'; }; }; document.onmouseup = function(ev){ var ev = ev || event; document.onmousemove = document.onmouseup = null; }; } }
说明:我这里处理的效果是,如果传一个参数,表示相对的对象是window对象,如果传2个参数,第一个是拖拽对象,第二个为相对父级
开篇就说了,搜房网的那个图片拖拽效果是我的一个心结,我写了一个类似的效果,供大家参考,因为自己没有买服务器,所以效果我就不展示了,直接把代码贴出来,供大家参考:
css:
<style> .box{ width:600px; height:400px; margin:50px auto; position:relative; overflow:hidden; } #box{ width:1000px; height:800px; position:absolute; left:50%; top:50%; margin:-400px 0 0 -500px; } #pic{ width:800px; height:600px; background:url(images/pic1.jpg) no-repeat; position:absolute; left:100px; top:100px; } #pic:hover{ cursor:move; } </style>
html:
<div class="box"> <div id="box"> <div id="pic"></div> </div> </div>
javascript:
window.onload = function(){ drag("pic","box"); function drag(obj,parentNode){ var obj = document.getElementById(obj); if(arguments.length == 1){ var parentNode = window.self; var pWidth = parentNode.innerWidth,pHeight = parentNode.innerHeight; }else{ var parentNode = document.getElementById(parentNode); var pWidth = parentNode.offsetWidth,pHeight = parentNode.offsetHeight; } obj.onmousedown = function(ev){ var ev = ev || event; var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop; var oWidth = obj.offsetWidth,oHeight = obj.offsetHeight; //阻止冒泡时间 ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true; document.onmousemove = function(ev){ var ev = ev || event; obj.style.left = ev.clientX - disX + 'px'; obj.style.top = ev.clientY - disY + 'px'; //左侧 if(obj.offsetLeft <=0){ obj.style.left = 0; }; //右侧 if(obj.offsetLeft >= pWidth - oWidth){ obj.style.left = pWidth - oWidth + 'px'; }; //上面 if(obj.offsetTop <= 0){ obj.style.top = 0; }; //下面 if(obj.offsetTop >= pHeight - oHeight){ obj.style.top = pHeight - oHeight + 'px'; }; }; document.onmouseup = function(ev){ var ev = ev || event; document.onmousemove = document.onmouseup = null; }; } } }
效果完全是用的那个封装代码块,引用起来也挺方便,有人会问了,你这用的id获取DOM元素,一个页面只能用一次啊,如果页面多次使用呢,有道理,解决方案之一,那就命名不同的id呗,又不犯法,方案二,获取id的地方改成获取class,但是要注意的是,getElementsByClassName是获取的class集合,需要改写一下,这里我就不写了,有兴趣的同学自行改写一下,好了,到这里真的结束了!