工作实践中封装过一个拖拽组件,仅以此文记录过程中碰到的一些问题、及思考角度,其中包含性能优化等知识点。
1. 事件选择?
答:Pointer events,优先drag event,是一类可以为定点设备所触发的DOM事件,被用来创建一个可以有效掌握各类输入设备(鼠标、触控笔和单点或多点的手指触摸)的统一的DOM事件模型。
2. 通过改变position还是translate做位移?
答:为了性能考虑,选择通过translate改变位移较好。
因为改变 position 是会导致浏览器进行重排,浏览器会重新计算受到影响的元素的几何位置。
而使用translate并且开启了3d硬件加速的时候(translate3d),就会强迫浏览器会为这个元素单独开启一个Layer,这个Layer是和原有页面的Layer隔离的,并且不会影响其他元素,所以就不会有重排,这种位置计算是很高效的。
参考文章:
3. 使用translate做位移时层级怎么办?
答:transform可以通过它的translateZ() 来改变元素的层叠顺序,值越大则层级越高。不过通过transform:translateZ() 改变元素z轴层级,必须在其父元素中设置transform-style:preserver-3d 或者在transform中设置perspective() 才有效。
参考文章:
4. 在组件A中引入封装的拖拽组件Draggable,但是希望被拖拽的组件的B的父组件是组件C,怎么办?
答:使用React.Portals,它提供了一种将子节点渲染到存在于父组件以外的DOM节点的优秀的方案。
5. 在4的基础上,希望父组件为C,且拖拽范围不是C的整体范围,只是其中的小部分范围?
答:父组件C提供可选参数limitedScope,限制拖拽范围。
6. 父组件C、被拖拽的组件B后期的大小会改变怎么办?
答:可通过ResizeObserver监视dom元素边界尺寸的变化。
7. onpointermove,用防抖(debounce)还是requestAnimationFrame哪个好?
答:requestAnimationFrame更适合,它不需要传入时间参数,是跟着浏览器的绘制频率自动调节的,可以防止动画失帧。但不兼容 IE9及以下浏览器。
8. 鼠标指针移动到window边界外,能正常拖动吗?
答:使用Element.setPointerCapture(),即使指针移动到window边界之外,也可以正常拖动
9. 怎么实现下图中的动态效果?
答:监听onpointermove事件的函数中不限制拖拽范围,监听到onpointerup事件后,判断是否超出边界(绿框),超出则立马设置组件B的位置最远处在边界上,不超出,即会出现图中的“回弹”效果。