Reputation: 822
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
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
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,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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:
As I tried so far, uses dragover solution seems work fine in both Chrome and Firefox.
Upvotes: 2