JavaScript图片或者div拖动拖动函数的实现

/*

* 拖动图片封装

html格式:<img lay-src=\”${item.Resourcesurl}\” alt=\”${item.ResourcesName}\” style=\”position: absolute;\” onmousemove=\”drag(this)\” >

*/

function drag(obj) {

/*实现div的拖拽功能

* 实现拖拽功能的思路

* 首先需要鼠标按下之后 onmousedown()

* div会跟随鼠标进行移动 onmousemove()

* 在鼠标松开之后,div会立刻固定位置 onmouseup()

*/

//当鼠标在被拖拽元素上按下时,开始拖拽 onmousedown

obj.onmousedown = function (event) {

//设置捕获所有鼠标按下的事件

/*

* setCapture()

* – 只有IE支持,但是在火狐中调用时不会报错,

* 而如果使用chrome调用,会报错

*/

/*if(box1.setCapture){

box1.setCapture();

}*/

obj.setCapture && obj.setCapture();

event = event || window.event;

//div的偏移量 鼠标.clentX – 元素.offsetLeft

//div的偏移量 鼠标.clentY – 元素.offsetTop

var ol = event.clientX – obj.offsetLeft;

var ot = event.clientY – obj.offsetTop;

//为document绑定一个onmousemove事件

document.onmousemove = function (event) {

event = event || window.event;

//当鼠标移动时被拖拽元素跟随鼠标移动 onmousemove

//获取鼠标的坐标

var left = event.clientX – ol;

var top = event.clientY – ot;

// console.log(left, top);

//修改box1的位置

obj.style.left = left + \”px\”;

obj.style.top = top + \”px\”;

};

//为document绑定一个鼠标松开事件

document.onmouseup = function () {

//当鼠标松开时,被拖拽元素固定在当前位置 onmouseup

//取消document的onmousemove事件

document.onmousemove = null;

//取消document的onmouseup事件

document.onmouseup = null;

//当鼠标松开时,取消对事件的捕获

obj.releaseCapture && obj.releaseCapture();

};

/*

* 当我们拖拽一个网页中的内容时,浏览器会默认去搜索引擎中搜索内容,

* 此时会导致拖拽功能的异常,这个是浏览器提供的默认行为,

* 如果不希望发生这个行为,则可以通过return false来取消默认行为

*

* 但是对IE8不起作用

*/

return false;

};

}

原生JS手写丝滑流畅的元素拖拽效果

提到元素拖拽,通常都会先想到用 HTML5 的拖拽放置 (Drag 和 Drop) 来实现,它提供了一套完整的事件机制,看起来似乎是首选的解决方案,但实际却不是那么美好,主要是它的样式太过简陋,无法实现更高级的用户体验:

这是浏览器默认的拖拽效果,点住拖拽任意图片或文字都会产生。

笔者因为之前有个小项目需要经常参考稿定设计,一直有留意其元素拖拽的效果(如下图),所以接下来我将以这种效果为蓝本,使用原生 JS 实现一个富有动感的 自定义拖拽 效果,话不多说直接开摸。

首先说下思路,我们需要知道鼠标的三个事件,分别是 mousedownmousemovemouseup ,当点击按下的时候,克隆一个绝对定位的元素,并标识下\”拖拽中\”的状态,接着在 mousemove 中就可以判断应该执行的具体方法,从而让元素随着鼠标移动起来。

在监听事件的 event 对象中,有几个参数是比较重要的:clientXclientY 标识的鼠标当前横坐标和纵坐标,offsetXoffsetY 表示相对偏移量,可以在 mousedown 鼠标按下时记录初始坐标,在 mouseup 鼠标抬起时判断是否在目标区域中,如果是则用鼠标获取到的当前的偏移量 – 初始坐标得到元素实际在目标区域中的位置。

为了阅读体验,以下所有代码均有部分省略,文末可查看完整源码地址,代码量并不多。

先简单实现一个两栏布局界面,并应用上一些 CSS 效果:

利用滤镜 filter: brightness(90%); 调节明亮度可以快速实现一个鼠标覆盖的动态效果,无需额外制作遮罩:

使用伪类激活 cursorgrabgrabbing 可以设置抓取动作的图标:

利用事件委托机制为选择列表添加 mousedown 事件监听,实现抓取的原理是在鼠标按下时克隆按下的元素,并把克隆出来的元素设置成绝对定位,让它\”浮\”起来:

将鼠标的坐标设置为克隆元素的绝对定位值(lefttop),就会像下图所示这样,此时减去 offset 偏移量,就能让克隆元素覆盖在本体上面。

初始化的值需要记录起来方便后续计算,同时我们用 dragging 变量标记了状态(拖动中),接下来配合移动鼠标的监听事件就能将元素“抓”起来了:

上面只是实现了元素的拖动,但是\”克隆\”的效果实在太明显了,为了让元素看起来更像是拖出来的而不是复制出来的,我们还要让本体隐藏,同时DOM结构不能丢失,这时只需在按下拖动时给本体元素设置个 opacity: 0,结束时再改回透明度1就能搞定。

虽然到这功能就算实现了,但实际效果还是有点僵硬,参考稿定设计中的元素放开时会固定回到一个位置,然后再收回去,这个过渡又有点鬼畜,不够流畅。其实只需让元素回退过程有一个自然地动画就行,transition 就能实现:

最终我在动作结束时给克隆元素添加了过渡属性,然后直接设置回初始坐标让克隆元素回到它的出生地点,用定时器在过渡动画持续的相同时间后移除克隆元素,这样就有了一个平滑稳定的回退动画。

由于在改变元素状态的过程中需要频繁进行多个 CSS 操作,为降低回流重绘的成本,最好将多个操作合并起来处理,这里利用了 cssText 来实现:

放大我们可以使用 transform: scale 来实现,只需要将拖动位置之间的距离当做变化系数(假设为d),那么scale变化数值即为(元素宽度 + d)/元素宽度,而放大的最终倍数必定为 图片实际宽度/元素的宽度,只要判断不超过这个边界就可以。(这个图片实际宽高在真实业务场景中建议在上传资源时就记录在数据库,这里我是模拟的随机一个原图尺寸)。

两点间距离计算公式为:

代码实现:

效果演示:

注意元素都要设置 transform-origin: top left; 改变缩放原点到左上角,否则默认(中心为原点)的转换会发生比较明显的偏移。

其实拖拽放置有点像是\”复制\”与\”粘贴\”,前面我们实现了复制,放置主要就是将元素粘贴到画布当中,流程步骤如下:

  1. 如果鼠标在目标区域,拷贝元素到画布中,如果不在画布中,执行倒退动画
  2. 2. 删除元素

判断是否在画布内抬起很简单,往画布上绑定mouseup监听事件即可,克隆的新元素必须删除无用的属性和class,此时设置元素的lefttop即可将元素放置进画布中,关键点在于画布内的target有可能是错的,因为如果鼠标抬起的区域已经放置了元素,那么相对偏移量就得我们自己计算了,使用getBoundingClientRect方法获取画布本身相对于视窗的偏移,鼠标坐标减去画布本身的偏移就是元素在画布中的位置了。

只贴了部分关键代码,完整代码文末查看。

如果不对边界情况进行处理可能会导致拖动时发生意外的中断,无法正确回收克隆元素。

参考稿定设计中元素拖拽是直接赋值原图的,原图大小通常无法控制,免不了需要加载时间,造成卡顿空白的问题,在网络不够快时体验尤其尴尬:

我的优化思路是利用浏览器加载过同一张图片就会优先读缓存的机制,先用一个Image加载原图,等其加载完毕再把拖拽元素的src改成原图,这样浏览器会\”自动\”帮我们优化这个过程,只需要注意一点,由于这是个异步任务,所以一定要做好对应标记,不然手速快的时候控制不好触发顺序。

效果演示,故意加大了图片的分辨率差异:

以上就是文章的全部内容,感谢看到这里,希望对你有所帮助或启发!创作不易,如果觉得文章写得不错,可以点赞收藏支持一下,也欢迎关注,我会更新更多实用的前端知识与技巧,我是茶无味的一天,期待与你共同成长~

[1] 完整代码地址: https://juejin.cn/post/7145447742515445791/#heading-9[2] 关于作者: https://book.palxp.com

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。