html5拖放+vue+localStorage,实现拖放排序并记录到本地

html5拖放+vue+localStorage,实现拖放排序并记录到本地

前言

因业务需求,需要在页面上列出多个视频,并能拖放排序,且记录排序。
现使用技术如下:
vue + html5的drag事件实现元素拖放排序 +l ocalStorage记录到本地浏览器上,刷新页面不会重置排序。

本示例为了更好的用户体验,排序后使用css3 的 translate 来变更元素位置,而不是2个元素直接交换内容!如果直接交换内容,则视频会重新加载,用户体验不好!

效果

实现效果如下:
在这里插入图片描述

在线演示地址:https://lujingtao.github.io/html5-drag-vue-localstorage/dist/index.html

技术要点

1、通过 draggable="true" 开启元素的排序功能,例如:<span draggable="true"><span>
然后绑定dragstart、dragenter、dragover...等事件,具体可以自行百度,其中本例也详细列出事件触发情况,按f12查看输出内容即可:
在这里插入图片描述

2、程序开始时从localStorage里面取排序记录,如果有则赋值给当前列表项目,其中还要处理列表项目变更情况(因为真实情况下,列表项目是从数据库读取的);

            //如果localStorage里面已有排序记录则赋值 this.sorts
            let sorts = []; //本地排序记录集合
            if (!!window.localStorage) {
                let listSort = window.localStorage["listSort"];
                if (listSort != undefined) {
                    sorts = JSON.parse(listSort);
                }
            }

            //如果有排序记录则设置排序,没有则顺序排序
            if (sorts.length == 0) {
                this.items.forEach((item, index) => {
                    item.sort = index;
                });
            } else {
                this.items.forEach((item) => {
                    for (let i = 0; i < sorts.length; i++) {
                        if (item.id == sorts[i].id) {
                            item.sort = sorts[i].sort;
                            break;
                        }
                        else if( i==sorts.length-1 ){
                            item.sort=sorts.length;
                        }
                    }
                });

                this.updateSort();
            }

3、当项目元素变更(删除项目)后,vue会更新项目(items)再更新项目元素(li),所以li的位置变更需要在vue的updated里面:

        updated() {
            //当项目集合变更后,触发li元素变更,再执行li的位置变更
            this.updateItemsSort();
        },

完整代码

gitHub地址: https://github.com/lujingtao/html5-drag-vue-localstorage

<template>
    <div id="app">
        <h1>html5拖放 + vue + localStorage,实现拖放排序并记录到本地</h1>
        <ul class="list">
            <li v-for="item in items" :key="item.id">
                <span draggable="true" :sort="item.sort">{{ item.name }}
                </span>
                <i class="close" :itemId="item.id">×</i>
            </li>
        </ul>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                items: [], //项目集合
            }
        },
        mounted() {
            this.updateItemsSort();
            //删除元素事件
            document.querySelectorAll(".list .close").forEach(c => {
                c.addEventListener("click", () => {
                    let id = c.getAttribute("itemId");
                    for (let i = 0; i < this.items.length; i++) {
                        if (this.items[i].id == id) {
                            this.items.splice(i, 1);
                            this.updateSort();
                            break;
                        }
                    }
                })
            })

            //拖拽元素集合
            let $dragList = document.querySelectorAll(".list span");
            let dragFrom = -1;
            let dragTo = -1;

            $dragList.forEach(v => {
                //拖动元素开始拖拽
                v.addEventListener('dragstart', (ev) => {
                    console.log("item" + ev.target.getAttribute("sort") + "_dragstart");
                    ev.dataTransfer.effectAllowed = "move";
                    //获取拖动元素排序
                    dragFrom = parseInt(ev.target.getAttribute("sort"));
                    //添加拖动元素当前样色
                    ev.target.classList.add("dragFrom");
                });

                //当拖动元素进入可释放目标时
                v.addEventListener('dragenter', (ev) => {
                    console.log("item" + ev.target.getAttribute("sort") + "_dragenter");
                    let _dragTo = ev.target.getAttribute("sort");

                    //添加目标元素当前样色
                    $dragList.forEach(item => {
                        item.classList.remove("dragTo");
                    });
                    if ( dragFrom != _dragTo){
                        ev.target.classList.add("dragTo");
                    }
                    ev.preventDefault();
                });

                //当拖动元素在可释放目标上时
                v.addEventListener('dragover', (ev) => {
                    console.log("item" + ev.target.getAttribute("sort") + "_dragover");
                    ev.preventDefault();
                });

                //当拖动元素在可释放目标上被释放时
                v.addEventListener('drop', (ev) => {
                    console.log("item" + ev.target.getAttribute("sort") + "_drop");
                    ev.preventDefault();
                    //获取目标元素id
                    dragTo = parseInt(ev.target.getAttribute("sort"));

                    if (dragFrom == dragTo) return;
                    console.log("%citem" + dragFrom + "交换item" + dragTo, "color:orange");

                    //交换排序
                    let temp = this.items[dragFrom].sort;
                    this.items[dragFrom].sort = this.items[dragTo].sort;
                    this.items[dragTo].sort = temp;

                    ev.target.classList.remove("dragTo");
                    this.updateItemsSort();

                });

                //拖动元素结束拖拽
                v.addEventListener('dragend', (ev) => {
                    console.log("item" + ev.target.getAttribute("sort") + "_dragend");
                    ev.target.classList.remove("dragFrom");
                    ev.preventDefault();
                });

                //当拖动元素变得不再是拖动操作的选中目标时
                v.addEventListener('dragexit', (ev) => {
                    console.log("item" + ev.target.getAttribute("sort") + "_dragexit");
                    ev.preventDefault();
                });

                //当拖动元素离开一个可释放目标时触发
                v.addEventListener('dragleave', (ev) => {
                    console.log("item" + ev.target.getAttribute("sort") + "_dragleave");
                    ev.preventDefault();
                });


            })
        },
        updated() {
            //当项目集合变更后,触发li元素变更,再执行li的位置变更
            this.updateItemsSort();
        },
        methods: {
            //通过translate改变排序位置
            updateItemsSort() {
                let $list = document.querySelectorAll(".list li");
                let _items = [];
                this.items.forEach((item, i) => {
                    let y = parseInt(item.sort / 3);
                    let x = item.sort % 3;
                    $list[i].setAttribute("style", "transform: translate( " + 100 * x + "%, " + 100 * y +
                        "%)");
                    _items.push({ id: item.id, sort: item.sort });
                });

                //存储排序
                if (!!window.localStorage) {
                    window.localStorage["listSort"] = JSON.stringify(_items);
                }
            },
            //更新排序
            updateSort() {
                //已有排序的先按现有排序排列
                this.items.sort((a, b) => {
                    return a.sort - b.sort
                });
                //再重新设置排序编号,以防元素个数发生变更
                this.items.forEach((item, index) => {
                    item.sort = index;
                });
            }
        },

        created() {
            this.items = [
                { id: 0, name: "item0" },
                { id: 1, name: "item1" },
                { id: 2, name: "item2" },
                { id: 3, name: "item3" },
                { id: 4, name: "item4" },
                { id: 5, name: "item5" },
                { id: 6, name: "item6" },
                { id: 7, name: "item7" },
                { id: 8, name: "item8" },
            ];

            //如果localStorage里面已有排序记录则赋值 this.sorts
            let sorts = []; //本地排序记录集合
            if (!!window.localStorage) {
                let listSort = window.localStorage["listSort"];
                if (listSort != undefined) {
                    sorts = JSON.parse(listSort);
                }
            }

            //如果有排序记录则设置排序,没有则顺序排序
            if (sorts.length == 0) {
                this.items.forEach((item, index) => {
                    item.sort = index;
                });
            } else {
                this.items.forEach((item) => {
                    for (let i = 0; i < sorts.length; i++) {
                        if (item.id == sorts[i].id) {
                            item.sort = sorts[i].sort;
                            break;
                        }
                        else if( i==sorts.length-1 ){
                            item.sort=sorts.length;
                        }
                    }
                });

                this.updateSort();
            }
        }
    }
</script>

<style>
    * {
        margin: 0;
        padding: 0;
    }

    h1 {
        height: 50px;
        line-height: 50px;
        font-size: 20px;
        text-align: center;
    }

    html,
    body,
    #app {
        height: 100%;
    }

    .list,
    .list li {
        list-style: none;
    }

    .list {
        position: absolute;
        left: 10px;
        right: 10px;
        top: 50px;
        bottom: 10px;
        overflow: hidden;

    }

    .list li {
        position: absolute;
        width: 33.33%;
        height: 33.33%;
        transition: all .4s;
    }

    .list span {
        position: absolute;
        left: 10px;
        top: 10px;
        right: 10px;
        bottom: 10px;
        line-height: 2;
        display: block;
        background: #000;
        color: #fff;
        font-size: 20px;
        text-align: center;
        cursor: move;
        transition: all .4s;
    }

    .list .close {
        position: absolute;
        right: 15px;
        top: 15px;
        width: 20px;
        height: 20px;
        border-radius: 20px;
        line-height: 17px;
        text-align: center;
        background: rgba(255, 255, 255, .5);
        font-style: normal;
        cursor: pointer;
    }

   .list .dragFrom {
       background: green;
    }
   .list .dragTo {
       background: green;
    }
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值