• home > webfront > SGML > html5 >

    html5鼠标拖动排序及resize实现方案分析及实践(drag&drop+resize)

    Author:[email protected] Date:

    拖放(Drag和 drop)是 HTML5 标准的组成部分。相比之前用jquery-UI等库实现,更加方便。使用css 设置resize属性,通过observeResize监听元素的大小改变。这个也是一个优化。

    对列表进行拖动排序,尺寸改变。之前一般会使用jQuery-UIinteractjs更加纯粹)。其通过mousedown、mousemove、mouseup这三个事件来实现页面元素被鼠标拖拽的效果。vue-drag-resize vuedraggable等包也大抵如此:https://codepen.io/lujun-zhou/pen/LYybXNx

    而HTML5中直接提供了拖放的API,只要通过监听元素的拖放事件就能实现各种拖放功能。

    拖放API阐释

    拖放(Drag和 drop)是 HTML5 标准的组成部分。相比之前用jquery-UI等库实现,更加方便(省去计坐标计算等)。

    为了使元素可拖动,必须把 draggable 属性设置为 true

    <div id=test draggable=true>test</div>

    draggable属性:设置元素是否可拖动。

    • true: 可以拖动

    • false: 禁止拖动

    • auto: 跟随浏览器定义是否可以拖动

    在HTML5标准中 文本、图片和链接是默认可以拖放的,它们的draggable属性自动被设置成了true。

    • 图片和链接按住鼠标左键选中,就可以拖放

    • 文本只有在被选中的情况下才能拖放。如果显示设置文本的draggable属性为true,按住鼠标左键也可以直接拖放。


    拖放事件事件详情

    一个元素被拖放,他可能会经过很多个元素上,最终到达想要放置的元素内。这里,我暂时把被拖放的元素称为源对象被经过的元素称为过程对象,到达的元素称为目标对象。不同的对象产生不同的拖放事件。

    源对象事件:

        dragstart:源对象开始拖放,开始移动时事件触发

        drag:源对象拖放过程中,移动被拖拽对象时触发

        dragend:源对象拖放结束,整个拖放操作结束时触发。

    过程对象事件:

        dragenter源对象进入过程对象范围内,被拖拽对象进入过程对象时被触发

        dragover源对象在过程对象范围内移动,被拖拽对象在过程对象内移动时触发

        dragleave源对象离开过程对象的范围,被拖拽对象离开目标对象时触发

    拖动事件列表

    每一个可拖动的元素,在拖动过程中,都会经历三个过程,拖动开始-->拖动过程中--> 拖动结束

    • 在拖动目标上触发事件 (源元素-被拖动的元素):

      • ondragstart - 在元素开始被拖动时候触发——拖动什么

      • ondrag - 在元素被拖动时反复触发

      • ondragend - 在拖动操作完成时触发

    • 释放目标时触发的事件(目的地对象):

      • ondragenter - 当被鼠标拖动的对象进入其容器范围内时触发此事件

      • ondragover - 当被拖动元素在目的地元素内时触发——放到何处

      • ondragleave - 当被拖动元素没有放下就离开目的地元素时触发

      • ondrop - 在一个拖动过程中,释放鼠标键时触发此事件——进行放置


    针对对象事件名称说明
    被拖动的元素dragstart在元素开始被拖动时候触发

    drag在元素被拖动时反复触发

    dragend在拖动操作完成时触发



    目的地对象dragenter当被拖动元素进入目的地元素所占据的屏幕空间时触发

    dragover当被拖动元素在目的地元素内时触发

    dragleave当被拖动元素没有放下就离开目的地元素时触发

    整个拖拽事件触发的顺序如下:dragstart-> drag -> dragenter -> dragover -> dragleave -> drop ->dragend


    目标对象事件:

        drop:源对象拖放到目标对象中,目标对象完全接受被拖拽对象时触发,可理解为在目标对象内松手时触发

    dragenter和dragover事件的默认行为是拒绝接受任何被拖放的元素。因此,我们必须阻止浏览器这种默认行为。e.preventDefault();

    如果drop接收盒子要想接收到元素,那么接收的拖动元素 dragenter和dragover必须阻止默认行为。

    el.ondragover = function(e){
       e.preventDefault();
    }
    el.ondrop = function(e){
     e.preventDefault();
    }

    在vue项目中:

    @dragstart.native="dragStart(index,$event)"
    @dragenter.native.prevent="dragenter(index,$event)"
    @dragleave.native.prevent
    @dragover.native.prevent
    @dragend.native="dragend(index,$event)"
    @drop.native.prevent="drop(index,$event)"

    这样才会触发drop

    dataTransfer对象

    在所有的拖放事件中都提供了一个数据传输对象dataTransfer,主要是用于在源对象和目标对象之间传递数据

    与拖放操作所触发的事件同时派发的对象是DragEvent,它派生于MouseEvent,具有Event与MouseEvent对象的所有功能,并增加了dataTransfer属性。该属性用于保存拖放的数据和交互信息,返回DataTransfer对象。

    DataTransfer对象定义的属性和方法有很多种,我们看下列入标准的几个。

    属性说明
    types只读属性。它返回一个我们在dragstart事件中设置的拖动数据格式的数组。 格式顺序与拖动操作中包含的数据顺序相同。
    files

    返回拖动操作中的文件列表。包含一个在数据传输上所有可用的本地文件列表。

    如果拖动操作不涉及拖动文件,此属性是一个空列表。

    dropEffect获取当前选定的拖放操作的类型或将操作设置为新类型。它应该始终设置成effectAllowed的可能值之一【none、move、copy、link】。dragover事件处理程序中针对放置目标来设置dropEffect
    effectAllowed指定拖放操作所允许的效果。必须是其中之一【 none, copy, copyLink, copyMove, link, linkMove, move, all, uninitialized】默认为uninitialized 表示允许所有的效果。ondragstart处理程序中设置effectAllowed属性



    方法说明
    void setData(format, data)将拖动操作的拖动数据设置为指定的数据和类型。format可以是MIME类型
    String getData(format)返回指定格式的数据,format与setData()中一致
    void clearData([format])删除给定类型的拖动操作的数据。如果给定类型的数据不存在,此方法不执行任何操作。如果不给定参数,则删除所有类型的数据。
    void setDragImage(img, xOffset, yOffset)指定一副图像,当拖动发生时,显示在光标下方。大多数情况下不用设置,因为被拖动的节点被创建成默认图片。x,y参数分别指示图像的水平、垂直偏移量


    dataTransfer方法

    setData(format, data)

    设置拖拽事件中要传递的数据,format的参数为数据类型,data要存入的数据。例如:event.dataTransfer.setData('text/plain', 'hello world')

    存入的数据类型,共有4种:

      • text/plain

      • text/html

      • text/xml

      • text/uri-list

    注:如果给定类型的数据不存在,则将其添加到拖动数据存储的末尾,使得dataTransfer.types列表中的最后一个项目将是新类型

    getData(format)

    该方法从dataTransfer对象中读取数据,参数为在setData方法中指定的数据类型,例如:event.dataTransfer.getData('text/plain')

    clearData()

    该方法清空dataTransfer对象中存储的数据,参数可选,为数据类型。若为空,则清空所有数据。

    setDragImage(element,x,y)

    • 该方法通过img元素来设置拖放图标

    • element表示拖拽时鼠标下面的图片(通常是image元素,也可以说canvas元素)

    • x、y分别指示相对于图片的横向和纵向偏移量,相对应鼠标指针。

    从拖动目标(dragstart事件触发的元素)生成半透明图像,并在拖动过程中跟随鼠标指针。这个图片是自动创建的,你不需要自己去创建它。然而,如果想要设置为自定义图像,那么 DataTransfer.setDragImage() 方法就能派上用场。

    图像通常是一个 <image> 元素,但也可以是<canvas> 或任何其他图像元素。

    这里面需要特别注意,element元素要在页面内是被渲染的

    具体可以查看:https://www.kryogenix.org/code/browser/custom-drag-image.html

    这个文章已经讲的非常清楚了。

    dataTransfer属性

    dropEffect 和 effectAllowed属性

    给指定拖放操作所允许的一个效果,例如:dataTransfer.effectAllowed = "move"。

    1. effectAllowed  用来指定当元素被拖放式所允许的视觉效果(作用于被拖放元素)。如果effectAllowed属性是定为none,则不允许拖放元素

    2. dropEffect   表示拖放操作的视觉效果(作用于目标元素),如果dropEffect 属性设定为none,则不允许被拖放到目标元素中。

    应该在dragstart事件中设置此属性,以便为拖动源设置所需的拖动效果。在 dragenter 和dragover 事件处理程序中,该属性将设置为在dragstart 事件期间分配的任何值,因此,可以使用effectAllowed来确定允许哪个效果。其值如下:

    • effectAllowed值

      • uninitialized:没有该被拖动元素放置行为。

      • none:被拖动的元素不能有任何行为。

      • copy:只允许值为“copy”的dropEffect。

      • link:只允许值为“link”的dropEffect。

      • move:只允许值为“move”的dropEffect。

      • copyLink:允许值为“copy”和“link”的dropEffect。

      • copyMove:允许值为“copy”和”link”的dropEffect。

      • linkMove:允许职位“link”和”move”的dropEffect。

      • all:允许任意dropEffect。

    • dropEffect值

      • none:不能把拖动的元素放在这里。这是除文本框之外所有元素的默认值。

      • move:应该把拖动的元素移动到放置目标

      • copy:应该把拖动的元素复制到放置目标

      • link:表示放置目标会打开拖动的元素(但拖动的元素必须是一个链接,有URL)

    chrome 默认是显示一个绿色的加号,设置:$event.dataTransfer.effectAllowed = 'move'。则恢复成默认鼠标样式

    files属性

    返回被拖拽的文件列表,是一个FileList对象,有length属性,可通过下标访问。此功能可用于将文件从用户桌面拖动到浏览器。一般配合FileReader来处理文件。如FileReader.readAsDataURL与FileReader.onload

    拖动元素排序实现

    之前写了两篇文章,有读者留言希望看代码,这次大致写了下

    https://codepen.io/lujun-zhou/pen/KKmNgxv

    vue实现

    https://codepen.io/lujun-zhou/pen/zYwozMZ

    magicBox tab 排序也是,就是采用这个方案。

    1626167489953.jpg

    画布元素位置与尺寸调整

    可以先回顾下《再谈BOM和DOM(6):dom对象及event对象位值计算—如offsetX/Top,clentX

    如果使用mouse事件控制,就显得非常复杂。这种一般都是使用第三方库实现,如  interact.js 、vue-drag-resize等。

    如果直接使用 css resize,然后通过 resizeObserve 回调尺寸改变,实现起来就变得简单的多。

    这种思路之前也介绍过,如《懒加载优化:JavaScript IntersectionObserver API监听元素是否可见》。

    这里的大致实现代码:

    https://codepen.io/lujun-zhou/pen/jOmVLGy

    之前做过一版类似DataV的需求,就是直接用html5 原生属性写的。

    canvas 画布实现:https://codepen.io/atindo23/pen/OJLbdrJ

    鼠标缩放元素操作

    缩放事件,实现不肖多说,MDN案列写的够好了

    https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onwheel



    参考文章:

    HTML5--拖放事件与dataTransfer对象 https://blog.csdn.net/hjc256/article/details/89021483

    说说 HTML 中的dropEffect 和 effectAllowed https://blog.csdn.net/gggg5643/article/details/52135824

    HTML5 进阶系列:拖放 API 实现拖放排序 - 林鑫的文章 - 知乎 https://zhuanlan.zhihu.com/p/26666141

    HTML5前端技术教程:H5拖放 - 方伟景的文章 - 知乎 https://zhuanlan.zhihu.com/p/47458212

    HTML5原生拖拽/拖放(drag & drop)详解 https://www.cnblogs.com/weiqinl/p/7886049.html

    HTML5 拖放(Drag 和 Drop)详解与实例 https://www.cnblogs.com/moqiutao/p/6365113.html






    转载本站文章《html5鼠标拖动排序及resize实现方案分析及实践(drag&drop+resize)》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/SGML/html5/2016_0124_434.html