主播你少说点话 發表於 2022-6-9 10:16:00

HTML5的拖拽功能--draggable

<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">&lt;div&gt;
&lt;div class="drag" draggable="true" id="dragger" ondragstart="handleDragStart(event)" ondrag="handleDrag(event)" ondragend="handleDragEnd(event)"&gt;
    拖动元素
&lt;/div&gt;
&lt;div class="drop" ondrop="handleDrop(event)" ondragenter="handleDragEnter(event)" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)"&gt;
    放置区域
&lt;/div&gt;
&lt;/div&gt;

var img = new Image();
img.src =
"https://p6-bd-official.byteimg.com/img/bytedance-cn/4ac74bbefc4455d0b350fff1fcd530c7~noop.image";
img.onload = () =&gt; {
console.log("图片加载成功!");
};

/* 拖拽元素响应事件 */
function handleDragStart(e) {
console.log("dragStart");
e.dataTransfer.setDragImage(img, 0, 0);
e.dataTransfer.setData("DRAG_NODE_ID", e.target.id);
}
function handleDrag(e) {
console.log("drag");
}
function handleDragEnd(e) {
console.log("dragEnd");
}

/* 放置元素响应事件 */
function handleDragEnter(e) {
console.log("dragEnter");
}
function handleDragOver(e) {
// console.log("dragOver");
e.preventDefault();
}
function handleDragLeave(e) {
console.log("dragLeave");
}
function handleDrop(e) {
console.log("drop");
e.preventDefault();
var data = e.dataTransfer.getData("DRAG_NODE_ID");
e.target.appendChild(document.getElementById(data));
}
</pre>
</div>
<div>
<h1 class="article-title" data-v-445aba1a="">前端里的拖拖拽拽</h1>
<div class="author-info-block" data-v-445aba1a=""><img src="https://p9-passport.byteacctimg.com/img/user-avatar/c6bc883edd20992c236b71409c0fe6e7~300x300.image">
<div class="author-info-box" data-v-445aba1a="">
<div class="author-name" data-v-445aba1a=""><span class="name" data-v-2ecffe9f="">DYBOY&nbsp;<span class="rank" data-v-3acffd25="" data-v-2ecffe9f=""><img alt="lv-3" data-v-3acffd25="" data-src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/e108c685147dfe1fb03d4a37257fb417.svg"></span></span></div>
<div class="meta-box" data-v-445aba1a="">2022年02月28日 10:52&nbsp;<span class="views-count" data-v-445aba1a="">·&nbsp;&nbsp;阅读 15824</span></div>
</div>
<button class="follow-button follow" data-v-565dd611="" data-v-445aba1a=""><span class="icon icon-follow" data-v-565dd611=""><span class="text" data-v-565dd611="" data-text="取消关注">关注</span></span></button></div>
<h2 data-id="heading-2"><img alt="前端里的拖拖拽拽" class="lazy article-hero lazyload" loading="lazy" data-v-0808752c="" data-v-445aba1a="" data-src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/693da9af96214183bf931b93b3985ff0~tplv-k3u1fbpfcp-zoom-crop-mark:1304:1304:1304:734.awebp?"></h2>
<div class="article-content" data-v-445aba1a="">
<div class="markdown-body">
<blockquote>
<p>最近在项目中使用了&nbsp;react-dnd,一个基于 HTML5 的拖拽库,“拖拽能力”丰富了前端的交互方式,基于拖拽能力,会扩展各种各样的拖拽反馈效果,因此有必要学习了解,最好的学习方式就是实操!</p>
</blockquote>
<p>拖拽交互常见于各种前端编辑器里,而“编辑器”是一个集成前端技术能力的综合性工程,其中就会涉及到各种形式的拖拽交互,因为“拖拽”是提升用户体验的重要交互方式,所以需要对拖拽的交互效果做各种定制化,作为开发者理应熟练掌握“拖拽”的应用!</p>
<p>最近在开发一款低代码平台,所以借此机会分享一下关于“拖拽”这一交互的基础知识和实践经验,希望可以给有需要的同学提供一点参考。</p>
<h1 data-id="heading-0">一、HTML5 中的拖放</h1>
<p>拖(Drag)和放(Drop)是 HTML5 标准的组成部分,了解掌握之后,举一反三,有助于提升我们在拖拽场景下技术方案的设计能力。</p>
<h2 data-id="heading-1">1.1 draggable 属性</h2>
<p>现代浏览器中,不难发现,图片标签(<code>&lt;img /&gt;</code>)是可以被长按拖拽,但如果需要自定义的 DOM 节点可以被拖拽需要配置以告诉浏览器提供对元素(<code>Element</code>&nbsp;/&nbsp;<code>Tag</code>)支持拖拽的能力。</p>
<p>而元素是否允许被拖放且可响应 API 操作依赖于&nbsp;draggable&nbsp;全局标签属性</p>
<p>draggable&nbsp;是一个布尔值类型的标签属性:</p>
<ul>
<li><code>true</code>:元素可被拖拽</li>
<li><code>false</code>:元素不可拖拽</li>
</ul>
<p>当元素设置了 draggable 属性,此时长按就可以自由拖拽了:</p>
<p><img alt="" class="medium-zoom-image lazyload" title="可拖拽效果" loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6809b3642c454837b5eda33d9feefe85~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp"></p>
<h2 data-id="heading-2">1.2 Drag &amp; Drop 事件</h2>
<p>HTML 的&nbsp;<code>drag</code>&nbsp;&amp;&nbsp;<code>drop</code>&nbsp;使用了“DOM Event”和从“Mouse Event”继承而来的“drag event” 。</p>
<p>一个典型的拖拽操作:&nbsp;用户选中一个可拖拽的(<code>draggable</code>)元素,并将其拖拽(鼠标按住不放)至一个可放置的(<code>droppable</code>)元素上,然后松开鼠标。</p>
<p>在拖动元素期间,一些与拖放相关的事件会被触发,像&nbsp;<code>drag</code>&nbsp;和&nbsp;<code>dragover</code>&nbsp;类型的事件会被频繁触发。</p>
<p>除了定义拖拽事件类型,每个事件类型还赋予了对应的事件处理器</p>
<table>
<thead>
<tr><th align="left">事件类型</th><th align="left">事件处理器</th><th align="left">触发时机</th><th align="left">绑定元素</th></tr>
</thead>
<tbody>
<tr>
<td align="left"><code>dragstart</code></td>
<td align="left">ondragstart</td>
<td align="left">当开始拖动一个元素时</td>
<td align="left">拖拽</td>
</tr>
<tr>
<td align="left"><code>drag</code></td>
<td align="left">ondrag</td>
<td align="left">当元素被拖动期间按一定频率触发</td>
<td align="left">拖拽</td>
</tr>
<tr>
<td align="left"><code>dragend</code></td>
<td align="left">ondragend</td>
<td align="left">当拖动的元素被释放(🖱️松开、按键盘 ESC)时</td>
<td align="left">拖拽</td>
</tr>
<tr>
<td align="left"><code>dragenter</code></td>
<td align="left">ondragenter</td>
<td align="left">当拖动元素到一个可释放目标元素时</td>
<td align="left">放置</td>
</tr>
<tr>
<td align="left"><code>dragexit</code></td>
<td align="left">ondragexit</td>
<td align="left">当元素变得不再是拖动操作的选中目标时</td>
<td align="left">放置</td>
</tr>
<tr>
<td align="left"><code>dragleave</code></td>
<td align="left">ondragleave</td>
<td align="left">当拖动元素离开一个可释放目标元素</td>
<td align="left">放置</td>
</tr>
<tr>
<td align="left"><code>dragover</code></td>
<td align="left">ondragover</td>
<td align="left">当元素被拖到一个可释放目标元素上时(100 ms/次)</td>
<td align="left">放置</td>
</tr>
<tr>
<td align="left"><code>drop</code></td>
<td align="left">ondrop</td>
<td align="left">当拖动元素在可释放目标元素上释放时</td>
<td align="left">放置</td>
</tr>
</tbody>
</table>
<p>各个事件的时机可以用下面这个图简单表示:</p>
<p><img alt="" class="medium-zoom-image lazyload" title="拖放事件图" loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3ac2bc4b8d7b4c3092864f898a3b597d~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp"></p>
<p>⚠️注意:&nbsp;dragOver&nbsp;事件的默认行为是:“Reset the current drag operation to "none"”。也就是说,如果不阻止放置元素的 dragOver 事件,则放置元素不会响应“拖动元素”的“放置行为”</p>
<pre><code class="hljs language-js copyable" lang="js"><span class="hljs-comment">// 让绑定该事件的元素支持放置
<span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">handleDragOver(<span class="hljs-params">e) {
<span class="hljs-comment">// 阻止默认的重置行为
<span class="hljs-comment">// 即可成为拖拽元素的放置区
e.preventDefault();
}
<span class="copy-code-btn">复制代码</span></span></span></span></span></span></span></span></code></pre>
<p>从设计事件标准来看,如果我们需要自行实现拖拽的效果,就需要从这关键的几个事件去思考设计。</p>
<h2 data-id="heading-3">1.3 DataTransfer</h2>
<p>在上述的事件类型中,不难发现,放置元素和拖动元素分别绑定了自己的事件,可如何将拖拽元素和放置元素建立联系以及传递数据?</p>
<p>这就涉及到&nbsp;<code>DataTransfer</code>&nbsp;对象:</p>
<blockquote>
<p><code>DataTransfer</code>&nbsp;对象用于保存拖动并放下(drag and drop)过程中的数据。它可以保存一项或多项数据,这些数据项可以是一种或者多种数据类型。 ——&nbsp;DataTransfer - MDN</p>
</blockquote>
<p><code>DataTransfer</code>&nbsp;对象在不同浏览器上因为标准可能不一样使得 API 有差异,但有几个“标准(常用)”属性和方法需要熟悉</p>
<p>在 Chrome 浏览器上的 DataTransfer 实例如下:</p>
<p><img alt="" class="medium-zoom-image lazyload" title="在 Chrome 浏览器上的 DataTransfer 的属性和方法" loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b201e470cf714a6ebcb7879c29d50b0d~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp"></p>
<h3 data-id="heading-4">(1) 属性</h3>
<table>
<thead>
<tr><th align="left">属性</th><th align="left">说明</th></tr>
</thead>
<tbody>
<tr>
<td align="left"><code>dropEffect</code></td>
<td align="left">获取当前选定的拖放操作类型或者设置的为一个新的类型。值为:none、copy、link、move</td>
</tr>
<tr>
<td align="left"><code>effectAllowed</code></td>
<td align="left">提供所有可用的操作类型。值是:none、copy、copyLink、copyMove、link、linkMove、move、all、uninitialized</td>
</tr>
<tr>
<td align="left"><code>files</code></td>
<td align="left">包含数据传输中可用的所有本地文件的列表。如果拖动操作不涉及拖动文件,则此属性为空列表</td>
</tr>
<tr>
<td align="left"><code>items</code>&nbsp;(只读)</td>
<td align="left">提供一个包含所有拖动数据列表的 DataTransferItemList 对象</td>
</tr>
<tr>
<td align="left"><code>types</code>&nbsp;(只读)</td>
<td align="left">提供一个 dragstart 事件中设置的格式的 strings 数组。</td>
</tr>
</tbody>
</table>
<h3 data-id="heading-5">(2) 方法</h3>
<table>
<thead>
<tr><th align="left">属性</th><th align="left">说明</th></tr>
</thead>
<tbody>
<tr>
<td align="left"><code>setData(format, value)</code></td>
<td align="left">设置给定类型的数据。如果该类型的数据不存在,则将其添加到末尾,以便类型列表中的最后一项将是新的格式。如果该类型的数据已经存在,则在相同位置替换现有数据。</td>
</tr>
<tr>
<td align="left"><code>getData(format)</code></td>
<td align="left">检索给定类型的数据,如果该类型的数据不存在或 data transfer 不包含数据,则返回空字符串</td>
</tr>
<tr>
<td align="left"><code>clearData()</code></td>
<td align="left">删除与给定类型关联的数据。类型参数是可选的。如果类型为空或未指定,则删除与所有类型关联的数据。如果指定类型的数据不存在,或者 data transfer 中不包含任何数据,则该方法不会产生任何效果。</td>
</tr>
<tr>
<td align="left">`setDragImage(img</td>
<td align="left">element, xOffset, yOffset)`</td>
</tr>
</tbody>
</table>
<p>在简单的拖拽场景中,其实可以类比&nbsp;window.localStorage&nbsp;对象的&nbsp;<code>setItem()</code>&nbsp;和&nbsp;<code>getItem()</code>&nbsp;方法来理解记忆.</p>
<p>但&nbsp;<code>getData()</code>&nbsp;在测试中发现只能在&nbsp;<code>ondrop</code>&nbsp;事件中获取到值:</p>
<p><img alt="image" class="medium-zoom-image lazyload" title="image" loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/220c8d6b604e4b4bbc4e738229a8ca3c~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp"></p>
<h2 data-id="heading-6">1.4 一个案例掌握拖放 API</h2>
<pre><code class="hljs language-html copyable" lang="html"><span class="hljs-tag">&lt;<span class="hljs-name">div&gt;
<span class="hljs-tag">&lt;<span class="hljs-name">div <span class="hljs-attr">class=<span class="hljs-string">"drag" <span class="hljs-attr">draggable=<span class="hljs-string">"true" <span class="hljs-attr">id=<span class="hljs-string">"dragger" <span class="hljs-attr">ondragstart=<span class="hljs-string">"handleDragStart(event)"&gt;拖动元素<span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;
<span class="hljs-tag">&lt;<span class="hljs-name">div <span class="hljs-attr">class=<span class="hljs-string">"drop" <span class="hljs-attr">ondrop=<span class="hljs-string">"handleDrop(event)" <span class="hljs-attr">ondragover=<span class="hljs-string">"allowDrop(event)"&gt;放置区域<span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;
<span class="hljs-tag">&lt;/<span class="hljs-name">div&gt;

<span class="hljs-tag">&lt;<span class="hljs-name">script&gt;<span class="javascript">
<span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">handleDragStart(<span class="hljs-params">e) {
    e.dataTransfer.setData(<span class="hljs-string">'DRAG_NODE_ID', e.target.id)
}
<span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">handleDragOver(<span class="hljs-params">e) {
    e.preventDefault();
}
<span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">handleDrop(<span class="hljs-params">e) {
    e.preventDefault();
    <span class="hljs-keyword">var data = e.dataTransfer.getData(<span class="hljs-string">'DRAG_NODE_ID');
    e.target.appendChild(<span class="hljs-built_in">document.getElementById(data));
}
<span class="hljs-tag">&lt;/<span class="hljs-name">script&gt;
<span class="copy-code-btn">复制代码</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>演示案例:&nbsp;codepen.io/DYBOY/pen/e…</p>
<p>效果:</p>
<p><img alt="演示" class="medium-zoom-image lazyload" title="演示效果" loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d95bc468582b4478b0b6d68a32b1fab4~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp"></p>
<p><img alt="拖拽演示效果" class="medium-zoom-image lazyload" title="拖拽演示效果" loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c5d2b574a6554acbb2b7ee85619df5ec~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp"></p>
<h2 data-id="heading-7">1.6 兼容性</h2>
<p>是 HTML5 标准提出的能力,因此各大浏览器厂商对于标准的支持有差异,其兼容性参考如下:</p>
<p><img alt="" class="medium-zoom-image lazyload" title="caniuse drag drop" loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8be2d309ef4a450989a0cadfb23f611f~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp"></p>
<p>相较于传统的通过鼠标事件:<code>mousedown</code>、<code>mousemove</code>、<code>mouseup</code>&nbsp;组合实现的拖拽要简单很多,少了放入目标边界的判断,也少了对位置的实时获取操作。</p>
<p>另外目前的 API 不算多,例如我们想要定制化拖拽的图片大小、鼠标样式等,目前暂时没发现比较方便的解决方式,但是从另一个角度来说,让我们对于拖拽能力的设计和标准有了一个更深切的认识,对于设计实现拖拽交互有了一个“理论”基础!</p>
<h1 data-id="heading-8">二、手搓一个</h1>
<p>有了上面的基础知识,那么实现一个列表拖拽排序并不是什么难事。</p>
<h2 data-id="heading-9">2.1 设计实现</h2>
<p>结合上述的 Drag &amp; Drop 的事件类型,那么拖拽排序主要是针对“拖动对象”之间相互作用关系的逻辑梳理,此处我们暂且区分为:</p>
<ul>
<li>源对象:&nbsp;拖拽列表中被拖动的单个列表项</li>
<li>目标对象:&nbsp;拖拽列表中和“源对象”产生“相互作用”的列表项</li>
</ul>
<p>整体的交互事件的设计思路如下:</p>
<h3 data-id="heading-10">(1) ondragstart</h3>
<p>此时开始拖拽“源对象”的时机,在此事件回调函数中改变“源对象”的样式,设置拖拽的一些传递参数等初始值。</p>
<pre><code class="hljs language-js copyable" lang="js"><span class="hljs-comment">// 源对象开始拖拽
<span class="hljs-keyword">const handleDragStart = <span class="hljs-function">(<span class="hljs-params">e: React.DragEvent&lt;HTMLDivElement&gt;) =&gt; {
e.dataTransfer.effectAllowed = <span class="hljs-string">"move";
setDragId(e.currentTarget.dataset.index); <span class="hljs-comment">// 从 dataset 获取拖拽项的 id
};
<span class="copy-code-btn">复制代码</span></span></span></span></span></span></span></code></pre>
<h3 data-id="heading-11">(2) ondragover</h3>
<p>正与拖拽中的“源对象”产生相互影响的目标对象,此时“源对象”处于“目标对象”的正上方,目标对象 100ms/次的频率调用“目标对象”的&nbsp;<code>ondragover</code>&nbsp;中声明的回调事件。</p>
<p>此时,我们会计算改变“源对象”和“目标对象”的位置。</p>
<pre><code class="hljs language-js copyable" lang="js"><span class="hljs-comment">// 源对象在目标对象上方时
<span class="hljs-keyword">const handleDragOver = <span class="hljs-function">(<span class="hljs-params">e: React.DragEvent&lt;HTMLDivElement&gt;) =&gt; {
e.preventDefault(); <span class="hljs-comment">// 允许放置,阻止默认事件
<span class="hljs-keyword">const dropId = e.currentTarget.dataset.index;
move(dragId, dropId); <span class="hljs-comment">// 改变原列表数据
};
<span class="copy-code-btn">复制代码</span></span></span></span></span></span></span></span></code></pre>
<h3 data-id="heading-12">(3) ondrag</h3>
<p>该事件作用于“源对象”,此时正处于拖拽过程中,此时可以改变源对象的&nbsp;<code>opacity</code>、<code>display(none)</code>、<code>visiblity</code>&nbsp;样式属性,如果在&nbsp;<code>dragstart</code>&nbsp;事件改变,则会导致拖拽拷贝对象丢失。</p>
<pre><code class="hljs language-js copyable" lang="js"><span class="hljs-comment">// 源对象被拖拽过程中
<span class="hljs-keyword">const handleDrag = <span class="hljs-function">(<span class="hljs-params">e: React.DragEvent&lt;HTMLDivElement&gt;) =&gt; {
e.currentTarget.style.opacity = <span class="hljs-string">"0";
};
<span class="copy-code-btn">复制代码</span></span></span></span></span></span></code></pre>
<h3 data-id="heading-13">(4) ondragend</h3>
<p>在松手完成“源对象”的放置时,主动调用绑定在“源对象”身上的事件,此时恢复更改的样式。</p>
<pre><code class="hljs language-js copyable" lang="js"><span class="hljs-comment">// 源对象被放置完成时
<span class="hljs-keyword">const handleDragEnd = <span class="hljs-function">(<span class="hljs-params">e: React.DragEvent&lt;HTMLDivElement&gt;) =&gt; {
e.currentTarget.style.opacity = <span class="hljs-string">"1";
};
<span class="copy-code-btn">复制代码</span></span></span></span></span></span></code></pre>
<h2 data-id="heading-14">2.2 实现效果</h2>
<p><img alt="" class="medium-zoom-image lazyload" title="演示效果" loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf398c2c3d5e4f88b0d6684e0bb9d04f~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp"></p>
<h2 data-id="heading-15">2.3 加点动画</h2>
<p>上面的实现中效果还算可以,但是少了拖拽项的切换过程动画,直接在 dragover 事件中通过&nbsp;<code>move(dragId, dropId)</code>&nbsp;方法直接修改了原列表数据的排序,导致切换突变。</p>
<p>借助&nbsp;<code>animation</code>&nbsp;新增 CSS 帧动画:</p>
<pre><code class="hljs language-css copyable" lang="css"><span class="hljs-keyword">@keyframes dropUp {
<span class="hljs-number">100% {
    <span class="hljs-attribute">transform: <span class="hljs-built_in">translateY(<span class="hljs-number">5px);
}
}

<span class="hljs-keyword">@keyframes dropDown {
<span class="hljs-number">100% {
    <span class="hljs-attribute">transform: <span class="hljs-built_in">translateY(-<span class="hljs-number">5px);
}
}

<span class="hljs-selector-class">.drop-up{
<span class="hljs-attribute">animation: dropUp <span class="hljs-number">0.3s ease-in-out forwards;
}
<span class="hljs-selector-class">.drop-down{
<span class="hljs-attribute">animation: dropDown <span class="hljs-number">0.3s ease-in-out forwards;
}
<span class="copy-code-btn">复制代码</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>同样的在&nbsp;<code>dragOver</code>&nbsp;事件中处理,新增逻辑代码:</p>
<pre><code class="hljs language-js copyable" lang="js"><span class="hljs-comment">// 源对象在目标对象上方时
<span class="hljs-keyword">const handleDragOver = <span class="hljs-function">(<span class="hljs-params">e: React.DragEvent&lt;HTMLDivElement&gt;) =&gt; {
...
<span class="hljs-comment">// 设置动画
<span class="hljs-keyword">const dropId = e.currentTarget.dataset.index;
<span class="hljs-keyword">const dragIndex = findIndex(listData, <span class="hljs-function">(<span class="hljs-params">i) =&gt; i.id === dragId);
<span class="hljs-keyword">const dropIndex = findIndex(listData, <span class="hljs-function">(<span class="hljs-params">i) =&gt; i.id === dropId);
<span class="hljs-comment">// 通过增加对应的 CSS class,实现视觉上的动画过渡
e.currentTarget.classList.remove(<span class="hljs-string">"drop-up", <span class="hljs-string">"drop-down");
<span class="hljs-keyword">if (dragIndex &lt; dropIndex) {
    e.currentTarget.classList.add(<span class="hljs-string">"drop-down");
} <span class="hljs-keyword">else <span class="hljs-keyword">if (dragIndex &gt; dropIndex) {
    e.currentTarget.classList.add(<span class="hljs-string">"drop-up");
}
...
};
<span class="copy-code-btn">复制代码</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>增加了动画的效果:</p>
<p><img alt="增加了动画的效果" class="medium-zoom-image lazyload" title="增加了动画的效果" loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c91401fe76364dc78d2097a1bce2a2a6~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp"></p>
<p>看起来似乎好一点了,当然大家可以去扩充动画的效果,亦或者借助三方动画库。</p>
<h1 data-id="heading-16">三、已有拖拽库</h1>
<p>目前主流的拖拽库有:</p>
<ul>
<li>react-dnd:&nbsp;github.com/react-dnd/r…</li>
<li>react-beautiful-dnd:&nbsp;github.com/atlassian/r…</li>
<li>sortablejs:&nbsp;sortablejs.github.io/Sortable/</li>
<li>react-sortable-hoc:&nbsp;github.com/clauderic/r…</li>
</ul>
<p>关于几者的差异,可以参阅:《关于react中使用拖拽插件的评测》</p>
<p><img alt="" class="medium-zoom-image lazyload" title="对比使用趋势图" loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4a53a4dd792a41a591140c402d7c93c4~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp"></p>
<h1 data-id="heading-17">四、总结</h1>
<p>由于低代码平台其实会有丰富的拖拽场景,从可扩展和兼容性上考虑,最终选择了&nbsp;<code>react-dnd</code>&nbsp;作为基础拖拽库,当然,在复杂的拖拽场景下,是需要自行扩展该拖拽库,上手难度相对会高一点,不过有了这些“拖拽知识”作为前置基础,那么扩展功能也就不是什么难事了。</p>
</div>
</div>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>https://juejin.cn/post/7069588012912361509#heading-15</p><br><br>
来源:https://www.cnblogs.com/guo-siqi/p/16358323.html
頁: [1]
查看完整版本: HTML5的拖拽功能--draggable