拖拽实现备忘:拖拽drag&拖放drop事件浅析

1、相关重点

  DataTransfer 对象:拖拽对象用来传递的媒介,使用一般为Event.dataTransfer。

  draggable 属性:就是标签元素要设置draggable=true,否则不会有效果,例如:

<div title="拖拽我" draggable="true">列表1</div>

  ondragstart 事件:当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖曳元素上

  ondragenter 事件:当拖曳元素进入目标元素的时候触发的事件,此事件作用在目标元素上。建议绑定于可拖放区域,该事件仅在进入拖放区域时触发,在其内部移动时不触发,离开某一可拖放区域后再进入时会再次触发

  ondragover 事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上

  ondrop 事件:被拖拽的元素在目标元素上同时鼠标放开触发的事件,此事件作用在目标元素上

  ondragend 事件:当拖拽完成后触发的事件,此事件作用在被拖曳元素上

  Event.preventDefault() 方法:阻止默认的一些事件方法等执行。在ondragover中一定要执行preventDefault(),否则ondrop事件不会被触发。另外,如果是从其他应用软件或是文件中拖东西进来,尤其是图片的时候,默认的动作是显示这个图片或是相关信息,并不是真的执行drop。此时需要用document的ondragover事件把它直接干掉。

  Event.effectAllowed 属性:就是拖拽的效果。

2、ondrop

  拖放事件,绑定于可拖放区域上。之所以把这个方法单独拎出来,是因为在使用该方法时存在一些注意事项。当我们这样使用时:

<div class="drop-field" @drop="drop"></div>

methods: {
    drop (event) {
        console.log('drop', event)
    }
}

  发现当我们将可拖拽元素拖放至此时,并没有触发事件。根据 MDN 的文档:

A listener for the dragenter and dragover events are used to indicate valid drop targets, that is, places where dragged items may be dropped. Most areas of a web page or application are not valid places to drop data. Thus, the default handling for these events is to not allow a drop.", hence the only way for the drop event to be fired is to first cancel the dragenter or dragover event.

  我们必须阻止某一 DOM 元素对 dragover 的默认行为,才能使 drop 事件在其上正确执行:

<div class="drop-field"
@drop="drop"
@dragover="dragover">
</div>

methods: {
    drop (event) {
        console.log('drop', event)
    },
    dragover (event) {
        event.preventDefault()
    }
}

  在 Vue 中,我们可以将组织默认行为的过程简写如下:@dragover="dragover" 改为: @dragover.prevent

3、DragEvent传递参数消息

  注意,无论是 dragxxx 或 drop 事件,其传递的参数都是 DragEvent。

  让我很费解的是,对于在拖放区绑定的 drop 事件而言,其 DragEvent 中竟然无法找到被拖拽元素。这也就意味着,不借助额外变量,drop 事件是无法知道被拖放者是什么的。但我们仍可以借助 DragEvent 中的 DataTransfer 来进行被拖放对象的消息传递。流程如下:

  (1)在被拖拽对象的 dropstart 事件中传递消息

dragstart (event) {
    console.log('dragstart', event)
    event.dataTransfer.setData('my-info', 'hello')
    event.dataTransfer.setData('my-extra-info', 'world')
}

  (2)在拖放区的 drop 事件中获取消息

drop (event) {
    console.log('drop', event)
    console.log(event.dataTransfer.getData('my-info'))
    console.log(event.dataTransfer.getData('my-extra-info'))
}

  (3)在被拖拽对象的 dragend 事件中清除消息

dragend (event) {
    console.log('dragend', event);
    event.dataTransfer.clearData()
}

  注意:

  (1)不能在被拖拽对象的 dragend 事件中传递消息

  在整个拖拽过程中,事件的先后顺序为:拖拽对象的 dropstart -- 拖放区的 drop -- 拖拽对象的 dropend

  因而,如果在 dragend 中传递消息,是不能被 drop 捕获的。

  (2)不能在被拖拽对象的 dragover 事件中传递消息

  如果我们在被拖拽对象的 dragover 事件中传递消息,由于 dragover 事件的作用对象是「可拖放区」,即此时,该 dragover 中的 DragEvent 是以「可拖放区」身份施加的,故而不会传递到 drop 中。

  (3)消息只能是 String 类型

  dataTransfer 中设置的消息( 即 setData 的第二个参数 )只能是字符串类型。如果想要传递对象,需要先进行序列化。

  (4)Vue 中事件参数

  在上面的代码中,如果我们在 @dragstart 中想传递一些参数,如下:@dragstart="dragstart(item)",就会遇到一个问题:默认传递的 DragEvent 参数丢失了。此时,我们需要使用 Vue 的特殊变量来实现事件参数的传递:

@dragstart="dragstart($event, item)"

  简单代码:

<template>
  <div class="img-dialog" id="img-dialog" v-if="imgShow" @click="close($event)" data-type="close">
    <i class="el-icon-error" @click="closeImg"></i>
    <div class="left page-btn" v-if="activeIndex > 0" @click="activeIndex--">
      <i class="el-icon-arrow-left"></i>
    </div>
    <img id="large-img"
    draggable="true"
    class="large-img"
    :class="{'scaled': isScaled}"
    :src="imgs[activeIndex]"
    @click="scaleImg"
    @dragstart="dragstart"
    @dragend="dragend" />
    <div class="right page-btn" v-if="activeIndex < imgs.length - 1" @click="activeIndex++">
      <i class="el-icon-arrow-right"></i>
    </div>
  </div>
</template>
<script>
  import { isMobile } from '@/utils'
  export default {
    data () {
      return {
        imgShow: false,
        activeIndex: 0,
        imgs: [],
        isScaled: false,
        scale: 1,
        clientY: '',
        dragX: 0,
        dragY: 0,
        marL: 0,
        marT: 0
      }
    },
    methods: {
      load () {
        this.$nextTick(_ => {
          document.addEventListener('keyup', this.keyupMethod, false)
          // imgDom.addEventListener('touchstart', function(e) {
          //   self.clientY  = event.touches[0].clientY 
          //   alert(self.clientY )
          // })
          // imgDom.addEventListener('touchend',function (e) {
          //   alert(event.touches[0].clientY )
          //   alert(self.clientY - event.touches[0].clientY )
          //   let _scale = self.scale += 0.3
          //   imgLarge.style.transform = `scale(${_scale})`
          // })
        })
      },
      close (e) {
        if (e.target.dataset.type === 'close') this.closeImg()
      },
      closeImg () {
        this.imgShow = false
        document.removeEventListener('keyup', this.keyupMethod, false)
        this.isScaled = false
      },
      scaleImg () {
        this.isScaled = !this.isScaled
      },
      // 键盘事件
      keyupMethod (e) {
        if (e.keyCode === 27) this.closeImg()
        if (e.keyCode === 37) {
          if (this.activeIndex === 0) return
          this.activeIndex--
          this.isScaled = false
          e.preventDefault()
        }
        if (e.keyCode === 39) {
          if (this.activeIndex === this.imgs.length - 1) return
          this.activeIndex++
          this.isScaled = false
          e.preventDefault()
        }
      },
      dragstart (e) {
        this.dragX = e.x
        this.dragY = e.y
      },
      dragend (e) {
        let _x = e.x - this.dragX
        let _y = e.y - this.dragY
        this.marL += _x
        this.marT += _y
        let imgDom = document.getElementById('large-img')
        imgDom.style.marginLeft = `${this.marL}px`
        imgDom.style.marginTop = `${this.marT}px`
      }
    }
  }
</script>
<style lang="stylus" scoped>
.img-dialog{
  position fixed
  width 100%
  height 100%
  background: rgba(0,0,0,.4);
  z-index 9999
  left 0
  top 0
  display flex
  align-items center
  justify-content center
  box-sizing border-box
  .el-icon-error{
    position fixed
    top 50px
    right 50px
    font-size 30px
    cursor pointer
  }
  .large-img{
    cursor zoom-in
    max-height 80%
    max-width 70%
    &.scaled{
      cursor zoom-out
      transform scale(1.5)
    }
  }
  .page-btn{
    position fixed
    width 40px
    height 40px
    border-radius 50%
    font-size 20px
    color #ffffff
    background rgba(0,0,0,.5)
    display flex
    align-items center
    justify-content center
    &.left{
      left 20px
    }
    &.right{
      right 20px
    }
  }
}
</style>

 

posted @ 2020-05-08 22:29  古兰精  阅读(2600)  评论(0编辑  收藏  举报