e.dataTransfer.setDragImage多选拖拽样式的实现

发生拖动时,浏览器会默认从拖动目标(dragstart事件触发的元素)生成半透明图像,并在拖动过程中跟随鼠标指针。这个图片是自动创建的,你不需要自己去创建它。然而,如果想要设置为自定义图像,那么 DataTransfer.setDragImage(img|element,xOffset,yOffset) 方法就能派上用场。

使用时需注意:1、setDragImage传入的第一个值img或者Element必须是页面中已经存在的元素,不然设置了也是白设置,然而设置不成功时浏览器将使用默认的拖动图片(所以自定义图像时DOM元素已经生成才可以)

2. 当拖拽父节点时,拖拽图片展示的是父节点和子节点的数据及整个节点; 如果想让只展示父节点数据(忽略子节点),则使用e.DataTransfer.setDragImage(e.target.children[0],0,0) 将第一个参数设置为子元素即可

应用: setDragImage对于任何非平凡的拖放用例都是IMO的重要特性.例如,考虑多选列表,其中拖动需要包括所有选定的项而不仅仅是拖动手势所在的行(简称多选拖拽), 下面就是项目中实现的效果。

多选拖拽的样式效果:

项目中是使用canvas绘制的, 然后转成图片进行显示, 下面是canvas的DOM节点:

<canvas id="myCanvas" width="275" height="160"></canvas>

列举一些canvas的设置:

const [transferTotalHeight, setTransferTotalHeight] = useState(0); // 拖拽图像总高度
const handleDragImage = (data: any[]) => {
    let canvas: canvasProps | null;
    let ctx: canvasProps;
    canvas = document.querySelector('#myRightCanvas');
    if (!canvas?.getContext) return;
    ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, 275, rightTransferTotalHeight); // 清除上次图像(重要, 不然容易产生        
    bug)
    // 计算
    let totalHeight = 5 * 32; // 设置最多显示5行
    setRTransferTotalHeight(totalHeight);
    // 整体填充颜色
    ctx.fillStyle = '#f5f5f5';
    // 整体填充位置及大小  x-横坐标 y-纵坐标 w-宽 h-高
    ctx.fillRect(0, 0, 275, totalHeight);
    // 整体描边颜色
    // ctx.strokeStyle = '#333';
    // // 整体描边位置及大小  x-横坐标 y-纵坐标 w-宽 h-高
    // ctx.strokeRect(0, 0, 275, totalHeight);
    let checkboxHeight = 8; // 初始复选框高度
    let titleHeight = 20; // 初始整体高度
    for (let i = 0; i < data.length; i++) {
      if (i > 0) {
        checkboxHeight += 32;
        titleHeight += 32;
      }
      // 如果选择数据超过5条,设置为省略号
      if (transferData.length > 5 && i >= 4) {
        ctx.beginPath(); // 起始一条路径,或重置当前路径
        ctx.arc(40, 144, 6, 0, Math.PI * 2, true); // 创建弧/曲线
        ctx.closePath(); // 创建从当前点回到起始点的路径
        ctx.fillStyle = 'rgb(0,0,0)'; // 设置或返回用于填充绘画的颜色、渐变或模式
        ctx.fill(); // 填充当前绘图(路径)
        ctx.beginPath(); // 起始一条路径,或重置当前路径
        ctx.arc(80, 144, 6, 0, Math.PI * 2, true); // 创建弧/曲线
        ctx.closePath(); // 创建从当前点回到起始点的路径
        ctx.fillStyle = 'rgb(0,0,0)'; // 设置或返回用于填充绘画的颜色、渐变或模式
        ctx.fill();
        ctx.beginPath(); // 起始一条路径,或重置当前路径
        ctx.arc(120, 144, 6, 0, Math.PI * 2, true); // 创建弧/曲线
        ctx.closePath(); // 创建从当前点回到起始点的路径
        ctx.fillStyle = 'rgb(0,0,0)'; // 设置或返回用于填充绘画的颜色、渐变或模式
        ctx.fill();
        break;
      }
      // 复选框填充及描边
      ctx.strokeStyle = '#333';
      ctx.lineWidth = 1; // 线宽
      ctx.fillStyle = '#1890ff';
      ctx.fillRect(8, checkboxHeight, 16, 16);
      // 对勾
      ctx.strokeStyle = '#eee';
      ctx.moveTo(12, checkboxHeight + 8);
      ctx.lineTo(16, checkboxHeight + 12);
      ctx.lineTo(16, checkboxHeight + 12);
      ctx.lineTo(20, checkboxHeight + 4);
      ctx.stroke();
      // ctx.rotate(0.5 * Math.PI);
      //文字对齐方式 start、end、center、left、right(选填)
      ctx.textAlign = 'start';
      //设置字体和尺寸 (必填)
      ctx.font = '400 12px PingFangSC-Regular';
      //填充文字 x-横坐标 y-纵坐标 (必填)
      ctx.fillStyle = 'rgba(0,0,0,0.85)';
      ctx.fillText(transferData[i].title, 32, titleHeight);
    }

    let img = new Image();
    let imgdata = canvas.toDataURL('image/png');
    let newdata = imgdata.replace(/^data:image\/png/, 'data:image/jpeg');
    img.src = newdata;
}

handleDragImage(dataList);

// dragStart事件
const handleDragStart = () => {
    ....
    e.dataTransfer.setDragImage(img, e.clientX - 380, 16);
    ...
}

踩了好多坑, 必须得记录一下...

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值