你知道前端的拖拽吗?
时间:2023-03-19 19:03:56
科技观察
最近在项目中使用了react-dnd[1],一个基于HTML5的拖拽类库。“拖动能力”丰富了前端交互方式。基于拖拽能力,会展开各种拖拽反馈效果,所以学习理解是很有必要的,最好的学习方法就是实践!拖拽交互在各种前端编辑器中很常见,而“编辑器”是一个综合前端技术能力的项目,会涉及到各种形式的拖拽交互,因为“拖拽”是一个重要的为了提高用户体验的交互方式,所以需要对拖放的交互效果进行各种定制,作为开发者应该熟练使用“拖放”!最近在开发一个低代码平台,借此机会分享一下“拖拽”交互的基础知识和实践经验,希望能为有需要的同学提供一些参考。一、HTML5中的拖放拖放是HTML5标准的一部分。在理解和掌握之后,举一反三,有助于提高我们在拖拽场景下技术方案的设计能力。1.1draggable属性在现代浏览器中不难发现image标签()可以长按拖动,但是如果需要自定义要拖动的DOM节点,就需要配置它告诉浏览器提供元素(Element/Tag)支持拖拽能力。元素是否允许拖拽和响应API操作取决于draggable[2]全局标签属性draggable是一个布尔标签属性:true:元素可以被拖拽false:元素不能被拖拽设置了draggable属性,然后长按可以自由拖放:1.2Darg&Drop事件HTML的拖放使用了“DOMEvent”和继承自“MouseEvent”的“dragevent”。一个典型的拖放操作:用户选中一个可拖动(draggable)的元素,并将它拖拽(按住鼠标)到一个可放下(droppable)的元素上,然后松开鼠标。在拖动元素的过程中,会触发一些与拖放相关的事件,比如drag和dragover类型的事件会被频繁触发。除了定义拖动事件类型外,每个事件类型还给出了相应的事件处理程序。每个事件的时序可以简单地用下图表示:??注意:dragOver事件的默认行为是:“将当前拖动操作重置为“无””。也就是说,如果不阻塞被拖放元素的dragOver事件,被拖放元素将不会响应“拖拽元素”的“拖放行为”//让绑定该事件的元素支持拖放函数handleDragOver(e){//阻止默认//的重置行为可以成为拖动元素的放置区e.preventDefault();}从设计事件标准来看,如果我们需要自己实现拖动的效果,我们需要考虑这些关键事件的设计。1.3DataTransfer在上面的事件类型中,不难发现拖放元素和拖动元素都绑定了各自的事件,但是如何在拖放元素和拖放元素之间建立关系并传递数据呢?这就涉及到DataTransfer对象:DataTransfer对象用来保存拖放(draganddrop)过程中的数据。它可以保存一项或多项数据,这些数据项可以是一种或多种数据类型。——DataTransfer-MDN[3]DataTransfer对象的API在不同的浏览器上可能会有所不同,因为标准可能不同,但是有几个“标准(常用)”的属性和方法是需要熟悉的。Chrome浏览器上的DataTransfer实例如下:(1)Attributes(2)Methods在简单的拖拽场景下,其实可以类比window.localStorage的setItem()和getItem()方法来理解内存目的。但是在测试中只能在ondrop事件中找到getData()得到的值:1.4一个案例掌握拖放APIdragelementdroparea
拖拽区域 <脚本>函数handleDragStart(e){e.dataTransfer.setData('DRAG_NODE_ID',e.target.id)}函数handleDragOver(e){e.preventDefault();}函数handleDrop(e){e.preventDefault();vardata=e.dataTransfer.getData('DRAG_NODE_ID');e.target.appendChild(document.getElementById(data));}演示案例:https://codepen.io/DYBOY/pen/eYeyvWm效果:演示拖拽演示效果1.6兼容性是HTML5标准能力提出的,所以各大浏览器厂商对标准的支持各不相同,各自的兼容性参考如下:对比传统鼠标事件:mousedown,mousemove和mouseup结合实现的拖拽就简单多了,不需要目标边界的判断,位置的实时获取。另外,目前的API也不算太多。比如我们要自定义拖拽图片的大小,鼠标样式等等,暂时还没有找到更方便的解决方案,但是换个角度,让我们对拖拽的设计和标准有了更深刻的理解和拖放功能,并具备《拖放交互的设计与实现》《理论》基础!2.用手搓一个。有了以上的基础知识,实现一个拖拽排序的列表就不难了。2.1设计与实现结合上述拖拽事件类型,拖拽排序主要是针对“拖拽对象”之间的交互关系进行逻辑梳理。被移动的单个列表项的目标对象:与拖动列表中的“源对象”“交互”的列表项整体交互事件的设计思路如下:(1)ondragstart开始拖动此时的“源对象”,在这个事件回调函数中改变“源对象”的样式,设置拖拽传递参数等一些初始值。//源对象开始拖动consthandleDragStart=(e:React.DragEvent
)=>{e.dataTransfer.effectAllowed="move";setDragId(e.currentTarget.dataset.index);//从数据集中获取拖拽项的id};(2)ondragover在拖拽中与“源对象”交互,“源对象”在“目标对象”的正上方,以100ms/次的频率调用目标对象在ondragover中声明的回调事件“目标对象”。至此,我们计算出“源对象”和“目标对象”在何处发生改变。//当源对象在目标对象之上时consthandleDragOver=(e:React.DragEvent)=>{e.preventDefault();//允许丢弃,防止默认事件constdropId=e.currentTarget.dataset.index;移动(dragId,dropId);//改变原来的列表数据};(3)ondrag该事件作用于“源对象”,正在拖拽过程中,此时可以改变源对象的opacity和display(none),visibilitystyle属性,如果在dragstart事件,会导致被拖拽的复制对象丢失。//当源对象被拖动时consthandleDrag=(e:React.DragEvent)=>{e.currentTarget.style.opacity="0";};(4)ondragend在放手后完成“源对象”放置时,主动调用“源对象”绑定的事件,此时恢复改变的样式。//放置源对象时consthandleDragEnd=(e:React.DragEvent)=>{e.currentTarget.style.opacity="1";};2.2实现效果2.3添加一些动画上面的效果还是可以的,但是缺少拖动项切换过程的动画,直接通过move(dragId,dragId,dragover事件中的dropId)方法,导致切换突然发生变化。添加CSS帧动画withanimation:@keyframesdropUp{100%{transform:translateY(5px);}}@keyframesdropDown{100%{变换:translateY(-5px);}}.drop-up{animation:dropUp0.3sease-in-outforwards;}.drop-down{animation:dropDown0.3sease-in-outforwards;}dragOver事件中同样处理,添加逻辑代码://当源对象在目标对象上方时consthandleDragOver=(e:React.DragEvent)=>{...//动画constdropId=e.currentTarget.dataset.index;constdragIndex=findIndex(listData,(i)=>i.id===dragId);constdropIndex=findIndex(listData,(i)=>i.id===dropId);//通过添加相应的CSS类,实现视觉动画过渡e.currentTarget.classList.remove("drop-up","drop-down");if(dragIndexdropIndex){e.currentTarget.classList.add("drop-up");}...};添加动画效果:添加动画效果好像更好。当然你可以扩展动画效果,或者使用第三方动画库。3.现有的拖拽库目前主流的拖拽库有:react-dnd:https://github.com/react-dnd/react-dnd/react-beautiful-dnd:https://github.com/atlassian/react-beautiful-dnd/sortablejs:https://sortablejs.github.io/Sortable/react-sortable-hoc:https://github.com/clauderic/react-sortable-hoc/关于它们之间的区别,你可以看到:《关于react中使用拖拽插件的评测[4]》4。总结由于低代码平台其实有丰富的拖拽场景,考虑到扩展性和兼容性,我最终选择了react-dnd作为基础拖拽库。当然,在复杂的拖放场景下,还需要自己扩展拖放库,入门难度相对较大,但是有了这些“拖放知识”作为前置基础,不难扩展功能。参考文献[1]react-dnd-Github:https://react-dnd.github.io/react-dnd/about[2]draggable-MDN:https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable[3]DataTransfer-MDN:https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer[4]使用拖放插件的评估反应:https://juejin.cn/post/6956112150989373448