列表拖拽转换的例子

列表拖拽转换的例子 今天看到有些表格中能够通过拖动来换行,手动排序的功能,想来自己实现一下,类似效果如下 image 主要解决问题的办法 drag元素 clientX和clientY 动画元素animition 解决思路 委托外部元素监听内容元素的(dragStart阶段,定位被拖动的元素) 委托外部元素监听元素已经被拉到哪个元素的位置 (使用dragover确定,停留在哪个元素的位置上) 为元素添加动画效果 利用node.appendChild相同元素实现元素转换,参考不使用append的remove使节点快速转移的方法 从一个简单的例子开始 image HTML
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
配置draggable=true,使元素变为滑动的 CSS ul { list-style: none; } li { padding: 30px 15px; background-color: cornflowerblue; color: #fff; border: 1px solid #333; border-radius: 10px; width: 30px; text-align: center; margin: 10px 0; transition: transform 2s ease; } JS逻辑 const con = document.getElementById("container"); let list = []; // 主要为了动画滑动的时候,用于控制滑动距离的 const init = () => { lists = Array.prototype.slice .call(document.querySelectorAll("li")) .map((element, index) => { element.dataset.index = index; return { index, offsetX: element.offsetLeft + con.offsetLeft, offsetY: element.offsetTop + con.offsetTop }; }); }; init(); let startX = 0, startY = 0; // 重置按钮的回调 let resetAll = () => { const list = Array.prototype.slice.call( document.querySelectorAll("li") ); list.forEach(item => { item.style.transform = "translate(0, 0)"; }); }; // 用于控制滑动到鼠标移动位置的函数 const changePosition = (node, relativeX = 0, relativeY = 0) => node && (node.style.transform = `translate(${relativeX}px, ${relativeY}px)`); // ondragstart中的event获取拖动元素的位置 con.ondragstart = function(event) { const target = event.target; startX = event.clientX; startY = event.clientY; }; // ondragend 拖动元素目前到达的位置 // 通过event.clientX和event.clientY可以获取鼠标松掉的位置 con.ondragend = event => { const node = event.target; const init = lists.find(item => item.index === +node.dataset.index); changePosition( node, event.clientX - init.offsetX, event.clientY - init.offsetY ); }; 关键点 使用dragstart获取元素的初始移动的位置 使用dragend获取元素最终拖动到鼠标落点的位置(用于后续动画移动) 这里注意的是clientX和clientY是元素相对视口中的位置,所以需要在元素原有位置的基础上进行适当的长度补偿即可 回到之前的例子 HTML
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
CSS ul { list-style: none; } .item { padding: 20px; width: 400px; background-color: cyan; color: red; border: 2px solid #333; margin: 20px 0; transition: transform 1s ease; text-align: center; } JS逻辑 在拖动时保存需要移动元素的对象,并且保存该元素的下一个元素(之后通过insertBefore来实现元素的交换) 使用dropover来更新需要与之发生交换的元素,记录下最终的交换元素 在dropend中对双方元素进行交换,先通过动画实现交换特效,加一个定时器,实现元素的交换(等到动画结束实现真正dom的交换) const con = document.querySelector(".container"); const dragObj = { nextObj: null, target: null }; const exchangeObj = { nextObj: null, target: null }; let timer; const changePosition = (node, relativeX = 0, relativeY = 0) => node && (node.style.transform = `translate(${relativeX}px, ${relativeY}px)`); function inserElem(exchange, target) { if (target.target === exchange.target) { return; } else { // nextObj没有说明与最后一个元素做交换 if (exchange.nextObj === null) { con.appendChild(target.target); } else { // 将现在这个元素插入到需要交换的前一个元素之前 // 这里对于同元素的insertBefore会直接执行在原来节点内元素的删除和新节点内元素的append不需要手动操作 con.insertBefore(target.target, exchange.nextObj); } } } // 记录目前正在拖动的元素 con.ondragstart = event => { dragObj.target = event.target; dragObj.nextObj = event.target.nextElementSibling; }; con.ondragend = event => { if (exchangeObj.target && dragObj.target) { // 先执行动画效果 changePosition( dragObj.target, 0, exchangeObj.target.offsetTop - dragObj.target.offsetTop ); changePosition( exchangeObj.target, 0, dragObj.target.offsetTop - exchangeObj.target.offsetTop ); if (timer) { clearTimeout(timer); }    // 动画结束后 定时器执行真正的dom交换 timer = setTimeout(() => { inserElem(dragObj, exchangeObj); inserElem(exchangeObj, dragObj); changePosition(dragObj.target, 0, 0); changePosition(exchangeObj.target, 0, 0); }, 1000); } }; // 记录下目前划过的需要交换的元素 con.ondragover = event => { exchangeObj.target = event.target; exchangeObj.nextObj = event.target.nextElementSibling; }; 最近,疫情没办法回学校,做了一个博客自己玩玩,作为一个React菜鸡练手项目,有兴趣的可以看看my-koa-react-blog

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):