• vue列表拖拽排序功能实现


    1.实现目标:目标是输入一个数组,生成一个列表;通过拖拽排序,拖拽结束后输出一个经过排序的数组。

    2.实现思路:

    2.1是使用HTML5的drag功能来实现,每次拖拽时直接操作Dom节点排序,拖拽结束后再根据实际的dom节点遍历得出新的数组。

    2.2使用mousedown,mouseover等鼠标事件来实现,每次监听事件时,仅改动列表项的样式transform,而不操作实际的dom顺序。拖拽结束时,根据transform计算数组项顺序,得出新数组用vue数据驱动的方式重绘列表,重置所有样式。

    总的来说就是可以通过不同的监听事件(drag、mouseover),按不同的顺序操作Dom(1.先操作实际dom,再添加动画,在输出数组;2。不操作实际dom,仅改变transfrom,得出新数组,用新数组生成新列表来更新节点)。

    3.实际代码

    3.1第一种实现

    html部分。(被拖拽的元素需要设置draggable=true,否则不会有效果)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <div id="app">
            <ul
            @dragstart="onDragStart"
            @dragover="onDragOver"
            @dragend="onDragEnd"
            ref="parentNode">
                <li
                v-for="(item,index) in data"
                :key="index"
                class="item"
                draggable="true"
                >{{item}}</li>
            </ul>
    </div>

      拖拽事件有两个对象(被拖拽对象和目标对象)。dragstart 事件: 当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖拽元素上。dragover事件:当拖拽元素穿过目标元素时候触发的事件,此事件作用在目标元素上。

    在拖拽事件开始时,将本次拖拽的对象保存到变量中。每当dragover事件,将目标对象保存到变量中,添加判断当目标对象和拖拽对象为不同的列表项时,交换两个dom元素的先后顺序。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    onDragStart(event){
         console.log("drag start")
         this.draging=event.target;
    },   
    onDragOver(event){
         console.log('drag move')
         this.target=event.target;
         if (this.target.nodeName === "LI" && this.target !== this.draging) {
            if(this._index(this.draging)<this._index(this.target)){
                this.target.parentNode.insertBefore(this.draging,this.target.nextSibling);
            }else{
                this.target.parentNode.insertBefore(this.draging,this.target);
            }
         }
    },
    onDragEnd(event){
            console.log('drag end')
            let currentNodes=Array.from(this.$refs.parentNode.childNodes);
      
            let data=currentNodes.map((i,index)=>{
                     let item=this.data.find(c=>c==i.innerText);
                     return item
              });
           console.log(data)
    },
    _index(el){
          let domData=Array.from(this.$refs.parentNode.childNodes);
          return domData.findIndex(i=>i.innerText==el.innerText);
    }

      

     现在基本效果有了,然后是添加动画。添加动画的方式是通过transform实现。

        因为每次拖拽排序触发时都会改变dom结构,为了实现移动的效果,可以在每次排序时先将dom节点恢复通过transform到原来的位置,使得表现上还是排序前的状态。然后添加transition,同时置空transform实现移动效果。(这里需要重绘才能触发效果,否则两次transform会直接抵消掉,可以使用setTimeout或者ele.offsetWidth来触发重绘),transform的偏移量可以通过改变节点顺序前后的距顶高度来获得。

    完整代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <style>
            ul{
                list-style:none;
                padding-bottom:20px;
            }
            .item{
                cursor: pointer;
                height:24px;
                line-height:24px;
                
                border:1px solid #d9d9d9;
                border-radius:4px;
                color:#fff;
                padding:10px;
            }
        </style>
      </head>
      <body>
        <div id="app">
            <ul
            @dragstart="onDragStart"
            @dragover="onDragOver"
            @dragend="onDragEnd"
            ref="parentNode">
                <li
                v-for="(item,index) in data"
                :key="index"
                class="item"
                draggable="true"
                >{{item}}</li>
            </ul>
        </div>
      </body>
      <script>
          var app = new Vue({
            el: '#app',
            data: {
                data:[1,2,3,4,5,6],
                draging:null,//被拖拽的对象
                target:null,//目标对象
            },
            mounted () {
                //为了防止火狐浏览器拖拽的时候以新标签打开,此代码真实有效
                document.body.ondrop = function (event) {
                    event.preventDefault();
                    event.stopPropagation();
                }
            },
            methods:{
                onDragStart(event){
                    console.log("drag start")
                    this.draging=event.target;
                },
                onDragOver(event){
                    console.log('drag move')
                    this.target=event.target;
                    let targetTop=event.target.getBoundingClientRect().top;
                    let dragingTop=this.draging.getBoundingClientRect().top;
                    if (this.target.nodeName === "LI"&&this.target !== this.draging) {
                        if (this.target) {
                            if (this.target.animated) {
                                return;
                            }
                        }
     
                        if(this._index(this.draging)<this._index(this.target)){
                            this.target.parentNode.insertBefore(this.draging,this.target.nextSibling);
                        }else{
                            this.target.parentNode.insertBefore(this.draging, this.target);
                        }
                        this._anim(targetTop,this.target);
                        this._anim(dragingTop,this.draging);
                    }
                },
                _anim(startPos,dom){
                    let offset=startPos-dom.getBoundingClientRect().top;
                    dom.style.transition="none";
                    dom.style.transform=`translateY(${offset}px)`;
     
                    //触发重绘
                    dom.offsetWidth;
              dom.style.transition="transform .3s";
              dom.style.transform=``;
    1
    2
    3
    4
    5
    //触发重绘
    // setTimeout(()=>{
    //     dom.style.transition="transform .3s";
    //     dom.style.transform=``;
    // },0)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
                    clearTimeout(dom.animated);
     
                    dom.animated=setTimeout(()=>{
                        dom.style.transition="";
                        dom.style.transform=``;
                        dom.animated=false;
                    },300)
                },
                onDragEnd(event){
                    console.log('drag end')
                    let currentNodes=Array.from(this.$refs.parentNode.childNodes);
                     
                    let data=currentNodes.map((i,index)=>{
                        let item=this.data.find(c=>c==i.innerText);
                        return item
                    });
                    console.log(data)
                },
                _index(el){
                    let domData=Array.from(this.$refs.parentNode.childNodes);
                    return domData.findIndex(i=>i.innerText==el.innerText);
                }
            }
        })
      </script>
    </html>

    3.2.第二种实现

     mousedown的时候记录下拖拽项和拖拽项初始位置,mouseover的时候将拖拽项和目标项交换位置,添加transform,mouseup的时候遍历出新数组来更新视图。这种方式就是动画不好加,个人瞎琢磨的,应该是思路错误了,放着看看吧。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <style>
            ul{
                list-style:none;
                padding-bottom:20px;
            }
            .item{
                cursor: pointer;
                height:24px;
                line-height:24px;
                
                border:1px solid #d9d9d9;
                border-radius:4px;
                color:#fff;
                padding:10px;
                user-select: none;
            }
        </style>
      </head>
      <body>
        <div id="app">
            <ul
            ref="parentNode"
            @mouseover="onMouseOver"
            @mouseup="onMouseUp">
                <li
                ref="li"
                v-for="(item,index) in data"
                :key="index"
                class="item"
                @mouseDown="(event)=>{onMouseDown(event,index)}"
                >{{item}}</li>
            </ul>
        </div>
      </body>
      <script>
          var app = new Vue({
            el: '#app',
            data: {
                data:[1,2,3,4,5,6],
                isDonw:false,
                draging:null,
                dragStartPos:0
            },
            mounted () {
                //为了防止火狐浏览器拖拽的时候以新标签打开,此代码真实有效
                document.body.ondrop = function (event) {
                    event.preventDefault();
                    event.stopPropagation();
                }
                document.onmouseup=()=>{
                    if(this.isDonw)
                        this.onMouseUp()
                };
            },
            computed:{
                nodes(){
                    return Array.from(this.$refs.parentNode.children)
                },
                itemHeight(){
                    return this.nodes[0].offsetHeight;
                }
            },
            methods:{
                onMouseDown(event,index){
                    this.isDonw=true;
                    this.draging=this.$refs['li'][index];
                    this.dragStartPos=this.draging.getBoundingClientRect().top;
                },
                onMouseOver(event){
                    if(this.isDonw){
                        let target=event.target;
                        let drag=this.draging;
                        let Index=this._index(target);
     
                        if(target.nodeName!='UL' && target!=drag){
                            let targetTop=target.getBoundingClientRect().top;
                            let dragTop=drag.getBoundingClientRect().top;
                            let targetOffset=targetTop-dragTop;
                            let dragOffset=targetTop-this.dragStartPos;
     
                            //样式变化
                            let targetStyle= target.style.transform;
                            let lastTransform=0;
                            if(targetStyle){
                                lastTransform=this.getTransform(targetStyle);
                            }
                            drag.style.transform=`translateY(${dragOffset}px)`;
                            target.style.transform=`translateY(${lastTransform-targetOffset}px)`;
     
                            
                        }
                    }
                },
                onMouseUp(){
                     
     
                    this.isDonw=false;
                    this.draging=null;
                    this.dragStartPos=0;
     
                    let res=[]
                    for(let i=0;i<this.nodes.length;i++){
                        let item=this.nodes[i];
                        let transform=this.getTransform(item.style.transform);
                        if(transform){
                            res[i+transform/this.itemHeight]=this.data[i];
                        }else{
                            res[i]=this.data[i];
                        }
                        item.style.transform='';
                        item.style.transition='';
                    }
                    this.data=[...res];
                    console.log(res)
                },
                getTransform(style){
                    if(style){
                        let firstIndex=style.indexOf('(')+1;
                        let lastIndex=style.indexOf(')')-2;
                        return parseInt(style.substring(firstIndex,lastIndex))
                    }
                },
                _index(el){
                    let domData=Array.from(this.$refs.parentNode.childNodes);
                    return domData.findIndex(i=>i.innerText==el.innerText);
                }
            }
        })
      </script>
    </html>

    引自:https://www.cnblogs.com/scdisplay/p/10431548.html

  • 相关阅读:
    技术人员转型秘笈
    2007 Office System Beta2 Technical Refresh 下载
    使用Word 2007写blog
    SharePoint WebPart 用户控件包装器 HandsOn Labs
    将Office SharePoint Server 2007 Beta2安装到DC上
    Visual Studio Tools for Office “v3” CTP!
    QuickPart : 用户控件包装器 for SharePoint 2007
    Office SharePoint Server 2007 !
    说说ASP.NET 2.0的书
    闫辉的书:《程序员,建立你的商业意识》
  • 原文地址:https://www.cnblogs.com/sweeeper/p/14283055.html
Copyright © 2020-2023  润新知