列表拖拽转换的例子

作者 : 开心源码 本文共4061个字,预计阅读时间需要11分钟 发布时间: 2022-05-13 共283人阅读

列表拖拽转换的例子

今天看到有些表格中能够通过拖动来换行,手动排序的功能,想来自己实现一下,相似效果如下

image

主要处理问题的办法

  • drag元素

  • clientX和clientY

  • 动画元素animition

处理思路

  • 委托外部元素监听内容元素的(dragStart阶段,定位被拖动的元素)

  • 委托外部元素监听元素已经被拉到哪个元素的位置 (使用dragover确定,停留在哪个元素的位置上)

  • 为元素增加动画效果

  • 利用node.appendChild相同元素实现元素转换,参考不使用append的remove使节点快速转移的方法

从一个简单的例子开始

image

HTML
<!---html部分---><ul id="container">    <li draggable="true">1</li>    <li draggable="true">2</li>    <li draggable="true">3</li>    <li draggable="true">4</li>    <li draggable="true">5</li>    <li draggable="true">6</li>    <li draggable="true">7</li>    <li draggable="true">8</li>    <li draggable="true">9</li>    <li draggable="true">10</li></ul><button onclick="resetAll()">Reset</button>
  • 配置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
<ul class="container">    <li class="item" draggable="true">1</li>    <li class="item" draggable="true">2</li>    <li class="item" draggable="true">3</li>    <li class="item" draggable="true">4</li>    <li class="item" draggable="true">5</li>    <li class="item" draggable="true">6</li>    <li class="item" draggable="true">7</li>    <li class="item" draggable="true">8</li>    <li class="item" draggable="true">9</li>    <li class="item" draggable="true">10</li></ul>
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

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 列表拖拽转换的例子

发表回复