html drag/drop setDragImage 不在第一次拖动时设置重影图像

html drag/drop setDragImage doesnt set ghost image on first drag

我正在尝试创建一个拖放菜单,用户可以在其中将图像缩略图从 div 拖到 canvas。

问题是来源 div 使用 sprite 显示其背景缩略图,所以我必须使用 setDragImage 允许在拖动 div 时显示图像。

我可以成功地将 div 拖到 canvas 并正常放下图像,但是我的问题是在拖动时重影图像直到我第二次拖动 div.

我使用之前答案中的代码:[HTML5 Drag and Drop events and setDragImage browser support

这是我对这段代码稍作修改的版本:

var isIE =  (typeof document.createElement("span").dragDrop === "function");


$.fn.customDragImage = function(options) {
    var offsetX = 0,
        offsetY = 0;

    var createDragImage = function($node, x, y) {
        var $img = $(options.createDragImage($node));
        icon = "icon" + window.draggedimgsrc;
        offsetX = window[icon][2] / 2;
        offsetY = window[icon][3] / 2;
        $img.css({
            "top": Math.max(0, y-offsetY)+"px",
            "left": Math.max(0, x-offsetX)+"px",
            "position": "absolute",
            "pointerEvents": "none"
        }).appendTo(document.body);

        setTimeout(function() {
            $img.remove();
        });

        return $img[0];
    };

    if (isIE) {
        $(this).on("mousedown", function(e) {
            var originalEvent = e.originalEvent,
                node = createDragImage($(this), originalEvent.pageX, originalEvent.pageY);
            node.dragDrop();
        });
    }

    $(this).on("dragstart", function(e) {
       var originalEvent = e.originalEvent,
           dt = originalEvent.dataTransfer;
        if (typeof dt.setDragImage === "function") {
            node = createDragImage($(this), originalEvent.pageX, originalEvent.pageY);
            console.log("node="+node);
            dt.setDragImage(node, offsetX, offsetY);  
        }
    });

    return this;

};

$("[draggable='true']").customDragImage({
    createDragImage: function($node) {
        //init icon [0] = icon filename | [1] = icon set | [2] = icon width | [3] = icon height
        icon = "icon" + window.draggedimgsrc;
        window.draggedimgset = window[icon][1];
        image="/boards/markers/soccerm/set" + window[icon][1] + "/" + window[icon][0] + ".png";
        return $node.clone().css("width", window[icon][2]).css("height", window[icon][3]).css("background", "transparent url(" + image + ") no-repeat center");        }
}).on("dragstart", function(e) {
    e.originalEvent.dataTransfer.setData("Text", "Foo");
});

奇怪的是,当我在 $node.clone() 上弹出一个边框时,它在我第一次拖动时就设置好了,它似乎并没有把图像放在那里。

我还手动输入了宽度和高度,所以我知道它不是图像的大小。

并且我在菜单出现之前预加载图像。

有什么想法吗?

所以这是旧的,但我也一直在努力解决这个问题。我发现问题是我在 dragstart 事件中创建了重影图像,我看到你也在做。

因此在您创建自定义重影图像时拖动已经开始,这意味着在您开始新的拖动之前它不会可见。

因此,只需尝试在 dragstart 之外调用 createDragImage。为我工作。

在事件侦听器之外预加载拖动反馈图像似乎可以避免拖动图像在第一次拖动时不出现的问题。有多种方法可以做到这一点,最好的方法在很大程度上取决于您究竟想做什么。以下是一种方法,其中拖动图像的 url 存储在可拖动元素的数据属性中,并且预先创建每个元素的拖动反馈图像并将其存储在对象中。

以下是 jQuery 和 vanilla JS 示例:

const images = {};
const draggable = $('div');
draggable.each((i, elem) => {
  const src = $(elem).data('src');
  const img = new Image();
  img.src = src;
  images[src] = img;
});

draggable.on('dragstart', (event) => {
  const src = $(event.currentTarget).data('src');
  const img = images[src];
  const drag = event.originalEvent.dataTransfer;
  drag.setDragImage(img, 0, 0);
  drag.setData('text/uri-list', src);
});
div {
  border: 1px solid #000000;
  height: 100px;
  width: 100px;
  line-height: 100px;
  text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div draggable="true" data-src="https://via.placeholder.com/50/0000FF">drag me</div>
<div draggable="true" data-src="https://via.placeholder.com/50/FF0000">drag me</div>

const images = {};
const elems = document.querySelectorAll('div');
for (const elem of elems) {
  const source = elem.dataset.src;
  const image = new Image();
  image.src = source;
  images[source] = image;
  elem.addEventListener('dragstart', (event) => {
    const src = event.currentTarget.dataset.src;
    const img = images[src];
    event.dataTransfer.setDragImage(img, 0, 0);
    event.dataTransfer.setData('text/uri-list', src);
  });
}
div {
  border: 1px solid #000000;
  height: 100px;
  width: 100px;
  line-height: 100px;
  text-align: center;
}
<div draggable="true" data-src="https://via.placeholder.com/50/0000FF">drag me</div>
<div draggable="true" data-src="https://via.placeholder.com/50/FF0000">drag me</div>

我同意这一点。我会添加一个小细节,也许对其他人有用。

当我在 onDragStart 事件中显示预览(尤其是带有图标或图像)时,我在我创建的用于管理应用程序中的 DnD 逻辑的专用 React Hook 中发现了同样的问题。

添加组件挂载时预览即可解决问题

但是,由于我不想一直在 DOM 中显示预览,所以我决定在组件安装时显示该元素几秒钟,然后假设 5 秒后我将其删除。

然后我将元素显示为onDragStart的第一个进程。由于缓存,它会按预期显示。

一个例子:

React.useEffect((
  createPreview(previewElement); // Will add the preview element to the document
  setTimeout(() => removePreview(previewElement), 5000); // will remove it, give it sometimes to load the images
), []);

const onStartDrag = (event) => {
  createPreview(previewElement);
  // code
  event.dataTransfer.setDragImage(previewElement, x, y);
};

const onDragEnd = () => {
  removePreview(previewElement);
};