拖拽實現備忘:拖拽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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM