【问题标题】:html drag/drop setDragImage doesnt set ghost image on first draghtml 拖放 setDragImage 不会在第一次拖动时设置重影图像
【发布时间】:2015-06-17 09:34:14
【问题描述】:

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

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

我可以成功地将 div 拖到画布上并很好地放置图像,但是我的问题是在拖动幽灵图像时直到我第二次拖动 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() 上弹出一个边框时,它在我第一次拖动时被设置,它似乎并没有将图像放在那里。

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

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

有什么想法吗?

【问题讨论】:

  • 有些东西在我看来很俗气。首先,您在$.extend 中定义函数的方式。我不认为这就是你的做法,所以在元素上实际调用customDragImage 时,你的自定义函数参数可能不正确。此外,您在该函数中有 3 个参数,但您只能使用 $node 调用它。毕竟,在你绑定了 dragstart 事件之后,也许可以在$(this) 上尝试trigger

标签: javascript jquery html drag-and-drop jquery-ui-draggable


【解决方案1】:

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

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

所以只需尝试在 dragstart 之外调用 createDragImage。为我工作。

【讨论】:

  • 我遇到了同样的问题,但就我而言,我只是想将拖动图像设置为页面上已经的元素,如下所示:@987654321 @。我不明白为什么它不起作用,因为元素已经存在。
【解决方案2】:

在事件侦听器之外预加载拖动反馈图像似乎可以避免拖动图像在第一次拖动时不出现的问题。有多种方法可以做到这一点,而最好的方法高度依赖于您正在尝试做的事情。以下是一种方法,其中拖动图像的 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>

【讨论】:

    【解决方案3】:

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

    当我在 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);
    };
    

    【讨论】:

      猜你喜欢
      • 2022-12-09
      • 1970-01-01
      • 2023-03-23
      • 2023-03-13
      • 1970-01-01
      • 1970-01-01
      • 2011-12-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多