zoukankan      html  css  js  c++  java
  • 探讨把一个元素从它所在的div 拖动到另一个div内的实现方法

    故事背景:

          接到一个新需求,要求用vue搞,主要是拖动实现布局,关键点有单个组件拖动,一行多列里面的组件拖动,  单个组件可以拖入一行多列里, 单个组件的拖动好实现,关键是把一个组件拖动到另一个类似于表格里面,而且有的情况下还需要限制拖动只能在水平方向,自己搜集资料, 实验,终于搞出来了。

    原理上主要分为两类:

    1. HTML5自带的拖放api,可用的库有 : Vue.Draggable  
    2. 使用js 监听鼠标的移动位置, 可用的库有:  jquery ui
    3. 使用point-event: none(下面会详细说明)

    各自缺点

    H5拖动的缺点:不能限制在水平 或垂直方向上拖动。

    使用原生js缺点:大量的dom操作,代码复杂(jquery ui 封装的比较好了,可直接用)

    但是问题来了,这次的需求是把基于jquery ui 的拖动 用 vue 重构,那么用vue.draggable吧, 但是需求里正好有一条是要限制在水平方向上拖动,尴尬,用不了。

    寻找方案

    最开始的尝试:

    <!DOCTYPE html>
    <html>
    <head>
        <title>vue结合原生js实现拖动</title>
    <script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>
    </head>
    <body>
    <div id="app">
    <div class="ctn ctn1">
        <div class="sub sub1" v-for="(site, index) in list1">
             <div class="dragCtn fixed" :style="{ left: X+'px', top: Y+'px'}"
               @mousedown="mousedown(site, $event)"
               @mousemove.prevent='mousemove(site, $event)'
               @mouseup='mouseup(site, $event)'>
                 拖动我
             </div>
       </div>
    </div>
    
    <div class="ctn ctn2">
        <div class="sub sub2" v-for="(site, index) in list2"
              @mouseenter='mouseenter(site, $event)'>
             <div class="dragCtn">
                 {{ index }} : {{ site.name }}
             </div>
       </div>
    </div>
       
    </div>
    
    <script>
    new Vue({
      el: '#app',
      data: {
        list1: [{name:1, index:0}],
        list2: [{name:'a', index:0}, {name:'b', index:1}, {name:'c', index: 2}, {name:'d', index: 3}],
        vm:'',
        sb_bkx: 0,
        sb_bky: 0,
        is_moving: false,
        X: 0,
        Y: 0
      },
      methods: {
          mousedown: function (site, event) {
            var startx=event.x;
            var starty=event.y;
            this.sb_bkx=startx - event.target.offsetLeft;
            this.sb_bky=starty - event.target.offsetTop;
            this.is_moving = true;
          },
          mousemove: function (site, event) {
              var endx=event.x - this.sb_bkx;
              var endy=event.y - this.sb_bky;
              var _this = this
              if(this.is_moving){
                this.X = endx;
                this.Y = endy;
          }
          },
          mouseup: function (e) {
            this.is_moving = false;
          },
        mouseenter: function (){
          console.log('鼠标进入')
        }
      }
    })
    </script>
    
    <style>
        .ctn{
            line-height: 50px;
            cursor: pointer;
            font-size: 20px;
            text-align: center;
            float: left;
        }
        .sub:hover{
            background: #e6dcdc;
            color: white;
            width: 100px;
        }
           .ctn1{
               border: 1px solid green;
               width: 100px;
           }
           .ctn2{
               border: 1px solid black;
               width: 100px;
               margin-left: 50px;
           }
           .fixed{
             width: 100px;
          position: fixed;
          background: red;
          left: 0;
          top: 0;
          cursor: move;
           }
    </style>
    </body>
    </html>

    就这样实现了基本的拖动,但是在拖动的时候,就不能触  mouseenter  事件了,而且鼠标必须拖动的很慢,移动快一点,拖动的div就跟不上了,这一点到现在还困惑,希望各位大侠指点。

    然后在网上找了很多资料,类库,但是不能完全符合我的需求,于是准备自己写了;

           首先是给组件添加mousedown事件,然后mousemove的时候 监听鼠标的位置,再赋值给组件,实现拖动,但是当组件拖动进入另一个元素的时候,无法监听mouseenter, 后来想的办法是给正在拖动的组件加上 point-event:none 属性,就是消除原有的鼠标事件,就可以触发其他组件的mouerenter事件了,point-event 属性的具体用法 可以参考这里:

    www.zhangxinxu.com/wordpress/2…

    因为拖动是用原生js写的,所以可以限制在水平方向拖动,再加上可以触发mouseenter事件,就正好实现的我的需求。

    伪代码如下:

    mousedown: function (event, site) {
            document.onmousemove=function (ev) {         
               // 移动的时候给元素增加 point-event:none 属性           
               ...        
        }       
         document.onmouseup=function (ev) {
               // up的时候 要移除point-event属性
               ...        
         }   
     }              

            但是后来上面要求,要兼容ie10,由于 point-event:none 是H5的属性,于是我赶紧去看看兼容性, 可怕的事发生了,point-event 属性只兼容到 ie11,完蛋!

             再想其他办法吧,没了思路,老版本的拖动是基于 jquery ui  的 ,于是去看了 jquery ui 的源码,看看它的拖动是怎么实现的。

    jquery ui 拖动实现原理

         不熟悉 jquery ui 的拖动方法的可以先看下  这里   

          看下面这段代码:

     $(function() {
        $( "#draggable" ).draggable();
        $( "#droppable" ).droppable({
          drop: function( event, ui ) {
            $( this ).html( "Dropped!" );
          }
        });
      });

          之前的关键问题就是怎么判断拖动的元素  $( "#draggable" )  在什么时候 进入了可以放置的区域      $( "#droppable" ) 的,看了源码,它的实现方式 简单来说就是拖动  $( "#draggable" )  的时候监听 鼠标的位置, 同时获取  $( "#droppable" )   的区域位置信息,只要鼠标进入该区域,就触发。

          顺着这个思路,做了实验 可以满足自己的需求 good !

          demo的实现效果如下:

    代码地址: https://github.com/YalongYan/vue-drag-layout

    拖动布局的实现方式应该还有更好的,欢迎大家提出更好的实现方式。

  • 相关阅读:
    Java基础——clone()方法浅析
    Unity shader error: “Too many texture interpolators would be used for ForwardBase pass”
    ar 解压一个.a文件报错: xxx.a is a fat file (use libtool(1) or lipo(1) and ar(1) on it)
    How to set up "lldb_codesign" certificate!
    Unity-iPhone has Conflicting Provisioning Settings
    ETC1/DXT1 compressed textures are not supported when publishing to iPhone
    Xcode 提交APP时遇到 &ldquo;has one iOS Distribution certificate but its private key is not installed&rdquo;
    XCode iOS之应用程序标题本地化
    苹果电脑(Mac mini或Macbook或iMac)恢复出厂设置
    Unity 4.7 导出工程在XCode10.1上编译报错
  • 原文地址:https://www.cnblogs.com/yalong/p/10036463.html
Copyright © 2011-2022 走看看