可拖动DIV层的实现方法

可拖动DIV层的实现方法

这几天做了一个英文单词搜索的谷歌扩展,其中的划词搜索功能会产生一个可托拖动的DIV层来作为结果显示,为了做一个较为完善的拖动层,花费了很长时间进行设计与调试。在此把心得总结了一下,讲讲实现方法与关键点。

先来看看效果:在线实例DEMO

基本思路

  1. 有一个DIV层,设定position属性为absolutefixed,通过更改其lefttop来更改层的相对位置。
  2. 在DIV层上绑定mousedown事件,设置一个拖动开始的标志为true,比如本例的isMouseDown,作为拖动的开始。
  3. document上绑定mousemove事件,移动鼠标时,根据拖动开始标志的真假,动态给DIV层设定根据当前鼠标位置的left,top值,实现按下鼠标后进行的拖动。
  4. 在DIV层上绑定mouseup事件,设置拖动开始标志为false,结束拖动。

写成代码,大概就是这个样子:

CSS

body,html{
  height:100%;
  width: 100%;
}


#draggable {
  background: #eee;
  display:flex;
  align-items: center;
  justify-content:center;
  position: fixed;
  left:0;top: 0;
  width: 200px;
  height:200px;
  font-size:2rem;
}

HTML

<div id="draggable">drag me</div>

Js

var isMouseDown;

draggable.addEventListener('mousedown', function() {
  isMouseDown = true;
})

document.addEventListener('mousemove', function(e) {
  if (isMouseDown) {
    draggable.style.left = e.clientX  + 'px';
    draggable.style.top = e.clientY + 'px';
  }
})

draggable.addEventListener('mouseup', function() {
  isMouseDown = false;
})

通过这样的设定,可以得到最基本的一个拖放DIV:

在线实例1:simple draggable div#1

操作体验一下,再来深入研究细节内容(接下来的实例相关代码不再重复,只增加不同内容)

进阶细节

拖动时鼠标于DIV的相对位置

实例1中最明显的问题就是,不论在何处点击DIV,拖动时DIV都会飞到以鼠标为原点的位置。这是因为单击鼠标时,鼠标在有一个相对于DIV的坐标位置,在紧接着的拖动操作中没有计算这个相对距离,修改代码如下:

draggable.addEventListener('mousedown', function(e) {
  isMouseDown = true;
  initX = e.offsetX;
  initY = e.offsetY;
})

document.addEventListener('mousemove', function(e) {
  if (isMouseDown) {
    var cx=e.clientX,cy=e.clientY;
    draggable.style.left = e.clientX - initX  + 'px';
    draggable.style.top = e.clientY - initY + 'px';
  }
})

在线实例2:simple draggable div#2

拖动时选中文字

实例2实现了在哪点击,在哪拖动的效果。但是如果拖动速度过快,就会出现文字被选中的情况,如果页面除了拖动层还有其他文字内容,那么就会出现只要拖动DIV,页面其余内容就会被选中的问题,此时需要增加一个CSS属性user-select:none,这个属性是防止元素被选中:

CSS:

.no-select {
  user-select: none;
}

HTML:

<div id="draggable" class="no-select">drag me</div>

同时,在开始拖动时,也要给body增加no-select的样式,并在拖动结束时,移除这个样式:

draggable.addEventListener('mousedown', function(e) {
  isMouseDown = true;
  document.body.classList.add('no-select');
  initX = e.offsetX;
  initY = e.offsetY;
})

draggable.addEventListener('mouseup', function() {
  isMouseDown = false;
  document.body.classList.remove('no-select');
})

这样就可以防止拖动DIV时选中其余的文字内容。

DIV被拖动到边界处,消失了,限定移动范围?

实例中仅有一个可拖动的方块演示起来倒不是很能说明问题。我在做扩展时,由于需要展示内容,拖动层设定为只有顶部可以拖动,当我拖动顶部到网页顶部时,问题出现了,我无法再拖动浮动层,只能关闭重新开启,而且当我启用固定在屏幕上功能时,拖动出边界后变无法关闭浮动层,只能刷新网页,于是我意识到拖动需要有范围限制。

逻辑很简单,当鼠标移动到边界时(页面内外部交界),会产生小于0或者大于网页宽度或高度的坐标,只要加入判断,鼠标超过边界时,手动规定窗口的lefttop 值,比如拖动时,当鼠标从页面最上端移出,鼠标Y坐标小于0时,就规定浮动层的top值为0。

其中有个很隐秘的经验,如果在documentmousemove事件上输出鼠标移动的坐标,当鼠标移动出浏览器是没有任何坐标信息的,但是如果是按下鼠标左键再去移动,移动出浏览器不松开鼠标的情况下,就可以看到输出的负值或大于网页宽高的鼠标坐标信息。这个坐标信息,是限制DIV拖动范围的关键。

计算临界情况的数学关系之后,需要用到DIV的宽高,在mousemove事件中增加判断,代码如下:

var height = draggable.offsetHeight,
    width = draggable.offsetWidth;

document.addEventListener('mousemove', function(e) {
  if (isMouseDown) {
    var cx = e.clientX - initX,
        cy = e.clientY - initY;
    if (cx < 0) {
      cx = 0;
    }
    if (cy < 0) {
      cy = 0;
    }
    if (window.innerWidth - e.clientX + mouseX < width) {
      cx = window.innerWidth - width;
    }
    if (e.clientY > window.innerHeight - height+ mouseY) {
      cy = window.innerHeight - height;
    }
    draggable.style.left = cx + 'px';
    draggable.style.top = cy + 'px';
  }
})

在线实例3:simple draggable div#3

拖动层失去焦点

在实例3的基础上尽情拖动,很快就会发现,如果按下鼠标时,移动出网页,虽然浮动层很好的被限定在了页面内,但是会出现松开鼠标,再次回到页面,浮动层会跟随鼠标移动的情况,相当于在页面外松开鼠标无效,没有结束拖动。

这个跟上面的情况有相似的地方,因为结束拖放的mouseup事件是绑定在DIV上,这就导致了该事件无法判断出鼠标是否移动到了页面外,根据上面的“隐秘的经验”,需要在document上也增加一个mouseup事件的绑定,来专门对付鼠标移出页面外时松开鼠标左键的情况:

document.addEventListener('mouseup', function(e) {
  if(e.clientY > window.innerHeight || e.clientY < 0 || e.clientX < 0 ||e.clientX > window.innerWidth){
    isMouseDown = false;
    document.body.classList.remove('no-select');
  }
});

终极难题:拖动层上放iframe

我最初卡壳的原因就是,查询完单词的结果显示界面是一个内嵌在拖动层上的iframe界面,我拖动DIV移动时,如果鼠标速度过快,移动到了iframe上,这时的鼠标事件就跑到了iframe中,原本的mousemove事件都监听不到鼠标了。

还有其他种种BUG都是由于iframe导致的,我尝试了很多关于iframe的方法,都无法实现。

最终根据给页面增加无法选择文本的CSS样式,依葫芦画瓢,只需给iframe层增加一个pointer-events:none的CSS样式。该样式是透明当前元素上的鼠标事件,也就是说,在元素上的鼠标事件相当于直接作用于下层的元素,该元素只有视觉上的展示,没有鼠标事件。

draggable.addEventListener('mousedown', function(e) {
	content.classList.add('pointer-events');
}

draggable.addEventListener('mouseup', function(e) {
	content.classList.remove('pointer-events');
}                                                   

最后,所有这些内容汇总在一起,正是开篇的DEMO

结束

效率有提升的空间,等我以后有机会学会优化了,再更新。

  • 22
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
在 Electron 中实现拖拽功能,可以通过在渲染进程中监听鼠标事件来实现。具体步骤如下: 1. 在 HTML 文件中添加拖拽区域,例如: ```html <div id="drag-area">拖拽区域</div> ``` 2. 在渲染进程中获取拖拽区域的 DOM 元素,并添加鼠标事件监听: ```javascript const dragArea = document.getElementById('drag-area'); dragArea.addEventListener('mousedown', (event) => { // 记录鼠标按下时的坐标 const startX = event.clientX; const startY = event.clientY; // 添加鼠标移动事件监听 document.addEventListener('mousemove', onMouseMove); // 添加鼠标松开事件监听 document.addEventListener('mouseup', onMouseUp); // 阻止默认事件和冒泡 event.preventDefault(); event.stopPropagation(); function onMouseMove(event) { // 计算鼠标移动的距离 const deltaX = event.clientX - startX; const deltaY = event.clientY - startY; // 设置窗口位置 const currentWindow = remote.getCurrentWindow(); const currentPosition = currentWindow.getPosition(); currentWindow.setPosition(currentPosition[0] + deltaX, currentPosition[1] + deltaY); // 阻止默认事件和冒泡 event.preventDefault(); event.stopPropagation(); } function onMouseUp(event) { // 移除鼠标移动事件监听 document.removeEventListener('mousemove', onMouseMove); // 移除鼠标松开事件监听 document.removeEventListener('mouseup', onMouseUp); // 阻止默认事件和冒泡 event.preventDefault(); event.stopPropagation(); } }); ``` 在上述代码中,我们通过监听 `mousedown` 事件来启动拖拽操作,记录鼠标按下时的坐标,并添加 `mousemove` 和 `mouseup` 事件监听。在 `mousemove` 事件处理函数中,计算鼠标移动的距离,并通过 `remote` 模块获取当前窗口对象,设置窗口位置。在 `mouseup` 事件处理函数中,移除事件监听。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值