如何防止Vuejs中dragstart时出现短暂闪烁

问题描述 投票:0回答:2

我正在尝试使 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>

期望的结果是一个可拖动的元素,并且在开始时不会闪烁。

javascript vue.js drag-and-drop
2个回答
2
投票

您的解决方案在 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,/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>

在 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。


0
投票

(3年后) 闪烁是因为设置为拖动图像时,拖动图像尚未渲染。您可以通过提前创建拖动图像(而不是在事件处理程序中)并将其存储在窗口外坐标来消除闪烁。

Chrome 在处理拖动图像的方式上存在缺陷(2023 年 9 月)。它们必须在 DOM 中,并且拖动图像包括 DOM 中其前后的任何内容的合成,通常是正文背景,因此透明度不起作用。

© www.soinside.com 2019 - 2024. All rights reserved.