我正在尝试使 DIV 可拖动。 除了 startdrag 时镜头闪烁之外,一切都运行良好。
这是问题的JSFiddle
以下是到目前为止的代码:
<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>
期望的结果是一个可拖动的元素,并且在开始时不会闪烁。
您的解决方案在 Firefox 和 Chrome 中存在不同的问题。
在 Firefox 中:当 dragstart 由于 e.pageX|Y
且
e.clientX|Y
始终为 0时,您会遇到 flicker 问题。查看 firefox Bugzilla #505521 了解更多详细信息。
解决方法是监听文档上的 dragover 事件,而不是可拖动元素中的 drag。
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>
在 Chrome 中:使用一个 setTimeout 来控制 Frame Rate 将改善渲染效果。
你会发现,如果 data property=
this.delay
小于或等于 10,则可拖动元素会更频繁地闪烁。如果 20 个或更多,则很少发生闪烁。
闪烁是由 DraggingEvent.pageX/Y 返回 (0, 0) 有时在拖动结束前的最后一帧引起的。我发现网上有人问类似的问题,但是没有好的答案。
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>
下面是一张 gif,它显示了 DND 在 Chrome 中的工作原理:
正如我到目前为止所尝试的那样,使用 dragover 解决方案似乎在这两个方面都工作得很好 Chrome 和 Firefox。
(3年后) 闪烁是因为设置为拖动图像时,拖动图像尚未渲染。您可以通过提前创建拖动图像(而不是在事件处理程序中)并将其存储在窗口外坐标来消除闪烁。
Chrome 在处理拖动图像的方式上存在缺陷(2023 年 9 月)。它们必须在 DOM 中,并且拖动图像包括 DOM 中其前后的任何内容的合成,通常是正文背景,因此透明度不起作用。