CSS之pointer-events优雅处理鼠标拖拽问题

1,447 阅读2分钟

pointer-events 除了指示该元素不是鼠标事件的目标之外,值none表示鼠标事件“穿透”该元素并且指定该元素“下面”的任何东西。基于这个特性,下面分享一下我是如何基于它来解决项目中的实际问题。

1、背景

我们项目有一个可视化低代码开发平台,通过拖拽控件的方法生成一个画面。编辑状态下,普通控件会在其表面添加一个透明的全遮罩层,方便实现鼠标的放大缩小(边框和四个角)功能,以及鼠标拖拽功能,大体布局如下。

说明一下,这里只是为了方便对比,普通控件不加遮罩也行,实际项目后来也移除了这个遮罩。

point-events.png

拖拽的流程:

最开始我认为当控件再添加到画面的那一刻,给控件添加三个独立的mousedown、mousemove、mouseup事件。 实际上不是这样的,直接给画布添加这3个事件,然后维护记录状态即可。

  1. 鼠标移入控件的任何位置,修改鼠标cursor: move;
  2. 触发mousedown,记录状态,该控件为拖拽前状态;
  3. 触发mousemove,若该控件被记录,则实时修改它的x、y值(绝对定位);
  4. 触发mouseup,更新并移除状态,释放拖拽;

2、问题

最近开发了一个iframe控件。即控件内部嵌入一个iframe,外部绑定一个url即可。iframe本身可能会产生滚动条,如果表面盖一层,那么就无法实现滚动。iframe本身的特殊性,天然沙箱隔离,iframe内部的鼠标事件与画布无关,只能在上面加一个半遮罩层,实现鼠标在部分区域进行拖拽,就会出现鼠标移除画布区域后,再回到画布区域,释放鼠标,mouseup事件并不会触发,因为它在iframe区域释放的,而不是在可拖拽的半遮罩区域内mouseup的;

为了方便理解,请看这幅图,1触发mousedown,并mousemove,2是离开了画布区域,3回到了画布区域,该控件仍然在拖拽状态,故会跟随鼠标移动,即使释放鼠标,仍然会跟随鼠标移动。

events2.png

2、解决

css属性pointer-events可以控制该元素是不是鼠标事件的目标。我们就给iframe控件再添加一个全遮罩,默认情况下,pointer-events: none;鼠标移入后,并不会产生遮挡,滚动条正常工作,当在可拖拽区域按下鼠标后,开始拖拽前把这个全遮罩修改为 pointer-events: auto; 及时鼠标离开控件后,只要再回到控件的任何位置,都能触发mouseup事件,从而释放拖拽动作,在结束拖拽后,再把全遮罩恢复到 pointer-events: none;这样修改的成本就最低了。