Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

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.

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.

I use this code from a previous answer: [HTML5 Drag and Drop events and setDragImage browser support

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.

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?

like image 224
Carl Miller Avatar asked Jun 17 '15 09:06

Carl Miller


2 Answers

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.

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. Worked for me.

like image 151
Ashley Oakes Avatar answered Sep 26 '22 06:09

Ashley Oakes


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.

Following are jQuery and vanilla JS examples:

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>
like image 34
benvc Avatar answered Sep 24 '22 06:09

benvc