我正在尝试创建一个拖放菜单,用户可以在其中将图像缩略图从 div 拖动到画布。
问题是源 div 使用精灵来显示其背景缩略图,因此我必须使用 setDragImage 来允许在拖动 div 时显示图像。
我可以成功地将 div 拖到画布上并很好地放置图像,但是我的问题是拖动重影图像时,直到我第二次拖动 div 时才显示。
我使用之前答案中的代码:[HTML5 拖放事件和 setDragImage 浏览器支持
这是我对这段代码稍作修改的版本:
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 示例...
JQuery:
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>
JavaScript:
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 事件中显示预览(特别是带有图标或图像)时,我在我创建的专用 React Hook 中发现了同样的问题,该 Hook 是为了管理我的应用程序中的 DnD 逻辑。
增加组件挂载时的预览即可解决问题。
但是,由于我不想始终将预览显示到 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);
};