Vue实现图片拖动互换位置(鼠标事件和拖拽事件的区别)

1,836 阅读4分钟

这里分为主文件和图片组件两个部分
话不多说,直接上代码

// index.vue
<template>
    <div class="content-pic-wrap">
        <ContentPic class="content-pic"
            v-for="(item, index) in picList" 
            :key="item"
            :index="index"
            :picSrc="item"
            @setCurDragImg="setCurDragImg"
            @clearCurDragImg="clearCurDragImg"
            @beHit="beHit"
        ></ContentPic>
    </div>
</template>
<script>
import ContentPic from './ContentPic.vue'
export default {
    data() {
        return {
            // 图片列表
            picList: [
                'http://xxxx1.jpg',
                'http://xxxx2.jpg',
                'http://xxxx3.jpg'
            ], 
            curImgIndex: null // 当前被拖动图片的index
        }
    },
    methods: {
        setCurDragImg(index){
            this.curImgIndex = index
        },
        clearCurDragImg(){
            this.curImgIndex = null
        },
        // 图片被撞击
        beHit(index){ // 被撞击图片的index
            if(index === this.curImgIndex) return
            let newList = [...this.form.picList]
            let curImg = this.form.picList[this.curImgIndex] // 当前拖动图片
            let hitImg = this.form.picList[index] // 被撞击的图片
            // 两张图片交换位置
            newList.splice(this.curImgIndex, 1, hitImg)
            newList.splice(index, 1, curImg)
            // 修改当前拖动图片的index,此时已经变成被撞击图片的位置了
            this.curImgIndex = index 
            this.picList = newList
        }
    },
    components: {
        ContentPic
    }
}
</script>

ContentPic.vue

<template>
  <div class="outer-pic" 
    @dragstart="dragStartHandler"
    @dragover.prevent="dragOverHandler"
    @dragenter="dragEnterHandler"
    @dragend="dragEndHandler">
    <img class="content-pic" :src="picSrc" alt="">
  </div>
</template>
<script>
export default {
  props:{
    index:{
      type: Number,
      required: true
    },
    picSrc:{
      type: String,
      required: true
    }
  },
  methods:{
    deleteImg(){
      this.$emit('deleteImg', this.index)
    },
    dragStartHandler(){
      // 设置当前正在被拖动的图片
      this.$emit('setCurDragImg', this.index)
    },
    dragEndHandler(e){
      this.$emit('clearCurDragImg')
    },
    dragOverHandler(e){
      // 在dragEnter中针对放置目标来设置
      e.dataTransfer.dropEffect = 'move'
    },
    dragEnterHandler(e){
      e.dataTransfer.effectAllowed = "move"
      this.$emit('beHit', this.index)
    },
    
  }
}
</script>

刚开始准备使用鼠标事件来进行碰撞检测的,这样会比较麻烦,因为伴随着x、y坐标以及元素大小的各种计算。
之后在找资料的时候,惊喜的发现HTML5新增了拖拽事件,也就是上述代码中使用的。
下面简述一下鼠标事件拖拽事件

鼠标事件

  • click:单击事件
  • dblclick:双击事件
  • mousedown:按下鼠标键时触发
  • mouseup:释放按下的鼠标键时触发
  • mousemove:鼠标移动事件
  • mouseover:移入事件
  • mouseout:移出事件
  • mouseenter:移入事件
  • mouseleave:移出事件
  • contextmenu:右键事件

mouseover事件和mouseenter事件,都是鼠标进入一个节点时触发。两者的区别是:mouseenter事件只触发一次,而只要鼠标在节点内部移动,mouseover事件会在子节点上触发多次。

<style>
    div{
      height: 100px;
      border: 1px solid #000;
    }
    p{
      border: 1px solid red;
    }
</style>
<body>
  <div id="page">
    <p id="word">its test</p>
  </div>
  <script>
    let div = document.getElementById('page')
    let p = document.getElementById('word')

    div.addEventListener('mouseover', function(e){
      console.log('div mouseover')
    }, false)
    div.addEventListener('mouseenter', function(e){
      console.log('div mouseenter')
    }, false)
    p.addEventListener('mouseover', function(e){
      console.log('p mouseover')
    }, false)
    p.addEventListener('mouseenter', function(e){
      console.log('p mouseenter')
    }, false)
  </script>
</body>

上述页面将鼠标从body移入div,会先后打印: div mouseover、div mouseenter
然后将鼠标从div移入p,会先后打印:p mouseover、div mouseover、p mouseenter
发现会多打印出一个 div mouseover,也就是说mouseover事件也会在自己的子节点中触发
这是因为事件冒泡导致的,如果不想让mouseover在子节点中触发,那么只需要在 p 的监听函数中阻止冒泡就行了。

p.addEventListener('mouseover', function(e){
  console.log('p mouseover')
  e.stopPropagation()
}, false)

鼠标事件属性

  • MouseEvent.altKey
  • MouseEvent.ctrlKey
  • MouseEvent.metaKey
  • MouseEvent.shiftKey

分别代表鼠标事件发生时,是否按下了对应的键盘按键,返回值是 true or false

document.body.addEventListener('click', function(e){
  let e = e || window.event
  console.log("altKey:"+e.altKey);            //是否按下alt键
  console.log("ctrlKey:"+e.ctrlKey);          //是否按下Ctrl键
  console.log("metaKey:"+e.metaKey);          //是否按下meta键
  console.log("shiftKey:"+e.shiftKey);        //是否按下shift键
}, false)

MouseEvent.button属性返回一个数值,表示事件发生时按下了鼠标的哪个键

  • 0代表左键
  • 1代表中键
  • 2代表右键
document.body.addEventListener('mousedown', function(e){
  e=e||window.event
  console.log(e.button)
}, false)

鼠标滚轮事件
滚轮事件在火狐浏览器中是DOMMouseScroll,而在其他浏览器中是onmousewheel。
火狐:e.detail 、向上滚动返回值为大于0、向下滚动返回值为小于0
非火狐:e.wheelDelta、向上滚动返回值为小于0、向下滚动返回值为大于0

function wheelEvent(e) {
  e = e || window.event;
  if (e.detail) {
    //判断是否支持e.detail    支持的话说明是火狐
    if (e.detail < 0) {
      console.log("向上滚动");
    } else {
      console.log("向下滚动");
    }
  } else {
    //不支持,说明是其他浏览器
    if (e.wheelDelta < 0) {
      console.log("向下滚动");
    } else {
      console.log("向上滚动");
    }
  }
}

document.body.onmousewheel = wheelEvent;
document.body.addEventListener("mousewheel", wheelEvent); //非火狐
document.body.addEventListener("DOMMous

拖拽事件

先来看看官方的解释:
拖放(Drag 和 drop)是 HTML5 标准的组成部分。
拖放是一种常见的特性,即抓取对象以后拖到另一个位置。
在 HTML5 中,拖放是标准的一部分,任何元素都能够拖放。
被拖拽的对象: ondragstart,ondrag,ondragend
拖拽释放区: ondragenter,ondragover(要触发drop事件,要在over里阻止浏览器的默认事件),ondragleave,ondrop(在释放时有浏览器的默认事件,比如释放图片,浏览器默认会在新窗口打开图片;要在drop事件里也阻止默认事件,ev.preventDefault();)
拖拽代码实例