简体   繁体   English

html 拖放 setDragImage 不会在第一次拖动时设置重影图像

[英]html drag/drop setDragImage doesnt set ghost image on first drag

I am trying to create a drag and drop menu where a user can drag an image thumbnail from a div to a canvas.我正在尝试创建一个拖放菜单,用户可以在其中将图像缩略图从 div 拖到画布上。

The issue is the source div uses a sprite to display its a background thumbnail, so I have to use the setDragImage to allow an image to be displayed whilst dragging the div.问题是源 div 使用精灵来显示其背景缩略图,因此我必须使用 setDragImage 来允许在拖动 div 的同时显示图像。

I can successfully drag the div to the canvas and drop the image fine, however my problem is whilst dragging the ghost image is not shown until the second time I drag the div.我可以成功地将 div 拖到画布上并将图像很好地放下,但是我的问题是拖动重影图像直到我第二次拖动 div 时才显示。

I use this code from a previous answer: [ HTML5 Drag and Drop events and setDragImage browser support我使用上一个答案中的代码:[ HTML5 拖放事件和 setDragImage 浏览器支持

and here's my slightly modified version of this code:这是我对这段代码稍加修改的版本:

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");
});

What's strange is that when I pop a border on the $node.clone() it gets set when i do the first drag it just doesn't seem to put the image in there.奇怪的是,当我在 $node.clone() 上弹出一个边框时,它会在我第一次拖动时设置,它似乎没有将图像放在那里。

I've also put a manual width and height in so I know its not the size of the image.我还手动设置了宽度和高度,所以我知道它不是图像的大小。

And I preload the image before the menu appears.我在菜单出现之前预加载图像。

Any ideas?有任何想法吗?

So this is old, but I've just been struggling with this as well.所以这是旧的,但我也一直在努力解决这个问题。 I found that the issue was that I was creating the ghost image inside the dragstart event, which I see you're doing as well.我发现问题在于我在 dragstart 事件中创建了重影图像,我看到您也在这样做。

So the drag would have already started by the time you create your custom ghost image, meaning it wont be visible until you start a new drag.因此,在您创建自定义重影图像时,拖动已经开始,这意味着在您开始新的拖动之前它是不可见的。

So just try calling createDragImage outside of the dragstart.因此,只需尝试在 dragstart 之外调用 createDragImage。 Worked for me.为我工作。

Pre-loading the drag feedback images outside of the event listener seems to avoid the issue where the drag image does not appear on the first drag.在事件侦听器之外预加载拖动反馈图像似乎可以避免第一次拖动时未出现拖动图像的问题。 There are a variety of ways to do this and the best one is highly dependent on exactly what you are trying to do.有多种方法可以做到这一点,最好的方法高度依赖于您正在尝试做什么。 Following is an approach, where the url for the drag image is stored in a data attribute on the draggable element and the drag feedback image for each element is created and stored in an object beforehand.以下是一种方法,其中拖动图像的 url 存储在可拖动元素的数据属性中,并且预先创建每个元素的拖动反馈图像并将其存储在对象中。

Following are jQuery and vanilla JS examples:以下是 jQuery 和 vanilla JS 示例:

 const draggable = $('div'); let images = {}; draggable.each((i, elem) => { let src = $(elem).data('src'); let img = new Image(); img.src = src; images[src] = img; }); draggable.on('dragstart', (event) => { let src = $(event.currentTarget).data('src'); let img = images[src]; let 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 elems = document.querySelectorAll('div'); let images = {}; for (let elem of elems) { let src = elem.dataset.src; let img = new Image(); img.src = src; images[src] = img; elem.addEventListener('dragstart', (event) => { let src = event.currentTarget.dataset.src; let 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>

I agree with this response .我同意这个回应 And I would add a small detail maybe it will be useful for someone else.我会添加一个小细节,也许对其他人有用。

I found the same problem in a dedicated React Hook that I created to manage my DnD logic in my App when I display the preview (especially with icons or images) in the onDragStart event.当我在 onDragStart 事件中显示预览(尤其是带有图标或图像)时,我在我创建的专用 React Hook 中发现了同样的问题,用于管理我的应用程序中的 DnD 逻辑。

Adding the preview when the component mount will solve the problem.添加组件挂载时的预览即可解决问题。

However, since I don't want display the preview into the DOM always, I decided to display the element for few seconds when the component mount then after let's say 5 seconds I remove it.但是,由于我不想始终将预览显示到 DOM 中,因此我决定在组件安装时显示该元素几秒钟,然后假设 5 秒钟后将其删除。

Then I display the element as the first process of onDragStart.然后我将元素显示为 onDragStart 的第一个进程。 It will be displayed as expected thanks to the cache.由于缓存,它将按预期显示。

An Example :一个例子 :

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);
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM