最近在使用canvs做布局图的设计,在使用到 DataTransfer.setDragImage()
的时候,查询了MDN和百度后,没有详细的解析,而且是有错误的情况,特结合代码和实际用例,给出合理的解决方案。
语法
void dataTransfer.setDragImage(img, xOffset, yOffset);
-
img | Element
用于拖曳反馈图像的图像
Element
元素。
如果 Element 是一个 img 元素,则将拖动位图设置为该元素的图像(保持大小);否则,将拖动数位图设置为从给定元素所生成的图片
-
xOffset
使用
long
指示相对于图片的横向偏移量 -
yOffset
使用
long
指示相对于图片的纵向偏移量
解析
- 发生拖动时,从拖动目标 (
dragstart
事件触发的元素) 生成半透明图像,并在拖动过程中跟随鼠标指针。这个图片是自动创建的,你不需要自己去创建它。然而,如果想要设置为自定义图像,那么DataTransfer.setDragImage()
方法就能派上用场。 - 图像通常是一个
<image>
元素,但也可以是<canvas>
或任何其他图像元素。该方法的 x 和 y 坐标是图像应该相对于鼠标指针出现的偏移量。 坐标指定鼠标指针相对于图片的偏移量。例如,要使图像居中,请使用图像宽度和高度的一半。通常在dragstart
事件处理程序中调用此方法。
实际用例
setDragImage
的第一个参数接受的是一个Element
参数,这样的话,普通的html元素、image元素、canvas都可以传递。
- 以官网例子为例,把canvas作为参数传递,我首先尝试的是这种方式,
发现并不能生效
。(官方的例子没有运行成功)
function dragWithCustomImage(event) {
var canvas = document.createElementNS("http://www.w3.org/1999/xhtml","canvas");
canvas.width = canvas.height = 50;
var ctx = canvas.getContext("2d");
ctx.lineWidth = 4;
ctx.moveTo(0, 0);
ctx.lineTo(50, 50);
ctx.moveTo(0, 50);
ctx.lineTo(50, 0);
ctx.stroke();
var dt = event.dataTransfer;
dt.setData('text/plain', 'Data to Drag');
dt.setDragImage(canvas, 25, 25);
}
2.根据案例,我接着使用HtmlDivElement
作为参数传递,创建了DIV元素,此时也没有生效
。
export function drawDragImage(dataTransfer: DataTransfer, context: string) {
const drawItem: API.EquipmentInfo = JSON.parse(context);
const div = document.createElement('div');
div.style.height = itemObj.height + 'px';
div.style.width = itemObj.width + 'px';
div.style.border = '1px solid #000';
const span = document.createElement('span');
span.innerText = '2222';
div.appendChild(span);
dataTransfer.setDragImage(div, drawItem.width / 2, drawItem.height / 2);
}
3.然后,我改进了canvas,把canvas转化为图片,第一次拖拽的时候,因为image加载元素是异步导致了没有生效,如图1;第二以后拖拽的时候可以生效,如图二。
const imageContent = canvas.toDataURL('image/jpeg', 1);
const image = new Image();
image.src = imageContent;
image.onload = () => {
console.log('image2 load');
};
dataTransfer.setDragImage(image, drawItem.width / 2, drawItem.height / 2);
图1
图2
function dragstart_handler(ev) {
console.log("dragStart");
// 设置拖动的格式和数据。使用事件目标的 id 作为数据
ev.dataTransfer.setData("text/plain", ev.target.id);
// 创建一个图像并且使用它作为拖动图像
// 请注意:改变 "example.gif" 为一个已经存在的图片
// 或者,一个还没有创建出来的图片,那么浏览器将会使用默认的拖动图片
// 译者注:默认的拖动图片与拖动对象没有联系。一般是一个小型文件图标
var img = new Image();
img.src = 'example.gif';
ev.dataTransfer.setDragImage(img, 10, 10);
}
解决方案
在尝试了不同方式设置拖拽反馈图像,总结了一些解决方案:
- 以html页面的元素为模版,动态生成内容,然后设置
Element
元素参数,可以设置DIV元素的z-index
,隐藏在实际页面之下:这样可以动态生成要拖拽的元素,并和生成的fabric的group保持一致。完美的解决了问题。
export function drawDragImage(dataTransfer: DataTransfer, context: string) {
const drawItem: API.EquipmentInfo = JSON.parse(context);
const dragElement = document.getElementById('dragItem');
const idElement = dragElement?.getElementsByClassName('dragItemId')[0];
const nameElement = dragElement?.getElementsByClassName('dragItemName')[0];
if (idElement) {
idElement.innerHTML = drawItem.id;
}
if (nameElement) {
nameElement.innerHTML = drawItem.typeName || '';
}
if (dragElement) {
dragElement.style.height = drawItem.height + 'px';
dragElement.style.width = drawItem.width + 'px';
dragElement.style.border = '1px solid #000';
dragElement.style.background = '#fff';
}
if (dragElement) {
dataTransfer.setDragImage(dragElement, drawItem.width / 2, drawItem.height / 2);
}
}