Home » vue » How do I prevent a short flickering at dragstart in Vuejs

How do I prevent a short flickering at dragstart in Vuejs

Posted by: admin November 26, 2021 Leave a comment

Questions:

I am trying to make a DIV draggable.
It all works quite well except for a shot flickering at startdrag.

Here is a JSFiddle of the issue

Below is the code so far:

<template>
  <div>    
    <div id="mydiv" :style="{ top: cordY + 'px', left: cordX + 'px' }">
      <div id="mydivheader" draggable @dragstart="dragStart" @drag="dragging" @dragend="dragEnd">DRAG</div>
          <p>BLA</p>
          <p>BLA</p>
          <p>BLA</p>
    </div>    
  </div>
</template>

<script>
export default {
  name: 'Home',
  data () {
    return {
      cordY: 200,
      cordX: 200,
      divY: 0,
      divX: 0
    }
  },
  methods: {
    dragStart: function (e) {
      var img = new Image()
      img.src = '../assets/nonexisting.png'
      e.dataTransfer.setDragImage(img, 10, 10)
      this.divX = e.pageX - e.target.getClientRects()[0].left
      this.divY = e.pageY - e.target.getClientRects()[0].top
      this.cordY = e.pageY - this.divY
      this.cordX = e.pageX - this.divX
    },
    dragging: function (e) {
      this.cordY = e.pageY - this.divY
      this.cordX = e.pageX - this.divX
    },
    dragEnd: function (e) {
      this.cordY = e.pageY - this.divY
      this.cordX = e.pageX - this.divX
    }
  }
}
</script>

The desired outcome is a draggable element without the flickering at the start.

Answers:

You solution has different issues in Firefox and Chrome.

In Firefox: you will meet the flicker issue when dragstart due to e.pageX|Y and e.clientX|Y is always zero. Look into firefox Bugzilla #505521 for more details.

The workaround is listen the dragover event on document instead of drag in dragable element.

new Vue({
  el: "#app",
  data: {
    cordY: 200,
    cordX: 200,
    divY: 0,
    divX: 0,
    delay: 20
  },
  mounted () {
    document.addEventListener('dragover', this.dragover, false) // remember to remove the event listener before some-when like Vue.beforeDestroy.
  },
  methods: {
    dragStart: function (e) {
      // https://learnvue.co/2020/01/how-to-add-drag-and-drop-to-your-vuejs-project/
      // HIDE GHOST/DRAG IMAGE BY REPLACING IT WITH A CUSTOM OR NONEXISTING IMAGE
      var img = new Image()
      img.src = 'data:image/jpeg;base64,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'
      e.dataTransfer.setDragImage(img, 10, 10)
      this.divX = e.pageX - e.target.getClientRects()[0].left
      this.divY = e.pageY - e.target.getClientRects()[0].top
      this.cordY = e.pageY - this.divY
      this.cordX = e.pageX - this.divX
    },
    dragover: function (e) {
      this.cordY = e.pageY - this.divY
      this.cordX = e.pageX - this.divX

    },
    dragEnd: function (e) {

    }
  }
})
#mydiv {
  position: absolute;
  width:150px;
  z-index: 9;
  background-color: #f1f1f1;
  border: 1px solid #d3d3d3;
  text-align: center;
  resize: both;
  overflow:auto
}

#mydivheader {
  padding: 10px;
  cursor: move;
  z-index: 10;
  background-color: #2196F3;
  color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div>
    <div id="mydiv" :style="{ top: cordY + 'px', left: cordX + 'px' }">
      <div id="mydivheader" draggable @dragstart="dragStart" @dragend="dragEnd">DRAG</div>
        <p>BLA</p>
        <p>BLA</p>
        <p>BLA</p>
      </div>
    </div>
</div>

In Chrome: Uses one setTimeout to control the Frame Rate will improve the rendering.

You will find if data property=this.delay is 10 or less, the draggable element will flicker more often. If 20 or more, the flicker happens rarely.

And the flicker is caused by DraggingEvent.pageX/Y return (0, 0) sometimes for last frame before drag ends. I found someone asked similar questions in the Internet, but there is no good answer.

new Vue({
  el: "#app",
  data: {
    cordY: 200,
    cordX: 200,
    divY: 0,
    divX: 0,
    timer: null,
    delay: 20,
    draging: false
  },
  methods: {
    dragStart: function (e) {
      // https://learnvue.co/2020/01/how-to-add-drag-and-drop-to-your-vuejs-project/
      // HIDE GHOST/DRAG IMAGE BY REPLACING IT WITH A CUSTOM OR NONEXISTING IMAGE
      var img = new Image()
      img.src = 'data:image/jpeg;base64,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'
      e.dataTransfer.setDragImage(img, 10, 10)
      this.divX = e.pageX - e.target.getClientRects()[0].left
      this.divY = e.pageY - e.target.getClientRects()[0].top
      this.cordY = e.pageY - this.divY
      this.cordX = e.pageX - this.divX
      this.draging = true
    },
    dragging: function (e) {
        if (this.timer) return;
      this.timer = setTimeout(() => {
        this.timer = null
        if (!this.draging) return
        this.cordY = e.pageY - this.divY
        this.cordX = e.pageX - this.divX
      }, this.delay)
    },
    dragEnd: function (e) {
        this.draging = false
      this.cordY = e.pageY - this.divY
      this.cordX = e.pageX - this.divX
    }
  }
})
#mydiv {
  position: absolute;
  width:150px;
  z-index: 9;
  background-color: #f1f1f1;
  border: 1px solid #d3d3d3;
  text-align: center;
  resize: both;
  overflow:auto
}

#mydivheader {
  padding: 10px;
  cursor: move;
  z-index: 10;
  background-color: #2196F3;
  color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div>
    <div id="mydiv" :style="{ top: cordY + 'px', left: cordX + 'px' }">
      <div id="mydivheader" draggable @dragstart="dragStart" @drag="dragging" @dragend="dragEnd">DRAG</div>
        <p>BLA</p>
        <p>BLA</p>
        <p>BLA</p>
      </div>
    </div>
</div>

Below is one gif it shows how the DND works in the Chrome: enter image description here

As I tried so far, uses dragover solution seems work fine in both
Chrome and Firefox.