MK01111000
MK01111000

Reputation: 822

How do I prevent a short flickering at dragstart in Vuejs

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.

Upvotes: 1

Views: 1682

Answers (2)

Bruce Perens K6BP
Bruce Perens K6BP

Reputation: 41

(3 years later) The flickering is because the drag image is not yet rendered when it is set to be the drag image. You can get rid of the flickering by creating a drag image in advance, instead of in the event handler, and storing it at an out-of-window coordinate.

Chrome is buggy in the way it handles drag images (September 2023). They must be in the DOM, and the drag image includes the composite of whatever is in front and behind it in the DOM, usually the body background, so transparency doesn't work.

Upvotes: 0

Sphinx
Sphinx

Reputation: 10729

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,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAwJCRcVExgWFhYaGBcaGB0dHh0XHxoeGhYXIR0lJSAdIB8mLTwxJik4Kh8gMkkzOD5AREVFJTBMUktNUj1DRUUBDQ4OExETJhUVJUUnJSdBQUFBQUFFQUFBQUFBQUFBQUFBQUFFQUFBQUFBRUFBRUFBQUFFQUFFQUVBQUFFQUFBQf/AABEIAGQAZAMBIgACEQEDEQH/xAAbAAACAwEBAQAAAAAAAAAAAAAEBQADBgIBB//EADQQAAEDAgMFBgQGAwAAAAAAAAEAAgMEERIhMQUGQVFhEyJxgZGxFDJioSNCU8HR8AdSov/EABgBAAMBAQAAAAAAAAAAAAAAAAECAwQA/8QAIREAAgICAgIDAQAAAAAAAAAAAAECEQMhEjEEQRQiURP/2gAMAwEAAhEDEQA/AE+zaMPJLhk3hzKcBiD2U4WLeOqZtCwy7NiJHQ9q0tw3dwsM0CNiz/pOyy0W/wB36djIQ63edr6pqIgTcqscVq2SlkpmA2dudLIQZO437p/DuRTjUFx8VpwF6qrGkTc2zOt3Opgb4L+ypl3KgLbN7py9s1qF4m4IHJmLbuOwOu5xIvkBy5I9u6NKRazh5rSqmVvJDhEPOR863j2C2mcCwkseTkfy9L+qV00oyYfLwWp3qeXNaDwcVmOyGvFZsiSkaIO1sK7I8lEJnzUSDgsbSDcZFMI6x3EArjsV2yJMIbHd6cvibwsSFpWrObtwhsd1ogVrj0jNLs7UXOJQOTCnSi5uh6uvihbilkbG3S7iBc8hzXHBN1y5DUdfFOzHDI2Rh/Mwhwv4hXErjjP7zUBkju3Ue3FZ6GiZhsbkra1zcTCOYWUlhLSVmzLdl8T1QIaBvAlRXF5UULLAAYrWRq+OK6IbGOS6wGg2SQIwmockdDLYAJrHIt66Mj7EO+G0nwsh/CfLE5zg9sTixzyG3Y24zte5IGuHyOc/x9JVtqntlxtiexz+zeSez7wwkA5tvdwtxtfgvoM7GSNLHtDmnUOAIPkqqWliiBETGsBNzhAFzzKNy69BtUey7YhbMIHSWkIuBY2tnnfTgVkd9YKztw+B7WxyU5hJcLhl3Ev7wBLS4YM/p6BaOXYtO+TtHRNLs/DPXLrx5o/HZDfo5OmYz/Hew5aUzOc68b2t0xBrngnNt9bC2fG/RblzlT2q5c9dv2Bu2cVDsktkgDgQSiJ5kGH5EpMq+o0HsWSR2NvZRXEKLGaS2kom4BJK7Azhzd4K8Mp3D8OQ4uTrZpHtOrMkgZezWjIdAqw4CxGRHJJdqw8TQQxm1wb2NiOXVN4ZBZZ2gnxXPNMmX4FXxeQkqkSni3aD5ZghPjRey9EZOp9FZFSxjVoPinl5MF1sRYmVfGrk1qM7GP8ATCrfCz/QIPyl+BWJllPIOKufI2yWlltCqnyO5p/kY6B/KR7NGXH5gB1uq5w0NAb1zOpVEktkLPNdo81mlmc3Xossaijt0gUQDnqIBAZsxj6WK4jqLFUUdWCLH5XZHoVJaCUG7Gl7OBaL+qSOtMo97HWzpLaZC+nJO4ZVlKF7m3DgRoc03p6pTmthRoGSKwPSllSrRUpDqGWNcOkQPxKrdUI2CgmSZBSzKqSdCSTLhqJPOh+37qpmffIJbVVlnYRwyVIK2BjB1ULr1IjVqK3EQCp5Sw5aHUHQphFXAfLI6M8syPUJI9/X7heCVvEhU42LdDwVlnXx4vI/um8M2Joc3T2KxwnbwIRdHtYwuuLEcQdCOqSWO1oZTNfHVK8VSS020o5Rf5Tyv+6KDeRWZxoqmmMfiV4ahA4HKBpQoIS6ZUukJXJLR8xHqlVdt8MJZGLuHEjIeA4powctIVySDaupETfrIy6dUiz8fFCuqnPOJ1yTzREZHL3WiMOKJ8rPc/6FFeCOv3XqNhoUEXXIgBUUVESZW+ID+heRsBKiiNgDGNw5gkI6lqCTb2yUUU2MhpG48z6ldP0OZ8yVFFEoKJ6lxNtPDVASQNuooqrRNlLmBuiKhfa3dB6m/wDKiidgQW2o+lv/AF/KiiiAx//Z'
      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.

Upvotes: 2

Related Questions