复制图片踩坑

1. 间接写入clipboard方式(document.execCommand())

 // 创建图片
 const img = document.createElement('img');
 img.src="https://github.githubassets.com/images/icons/emoji/unicode/270d.png"
 const body = document.body;
 body.appendChild(img);
 // 创建DOM范围
 const myrange = document.createRange();
 myrange.setStartBefore(img);
 myrange.setEndAfter(img);
 myrange.selectNode(img);
 // 添加到选区
 const selection = window.getSelection();
 selection.removeAllRanges();
 selection.addRange(myrange);
 // 复制图片
 document.execCommand('copy');
 // 删除图片清除选区
 selection.removeAllRanges();
 body.removeChild(img);

这种方式主要用到的是浏览器的文本框相关事件,如果做过富文本编辑器的同学会比较熟悉。 使用DOM2提供的 createRange()方法创建一个DOM范围,并使用 setStartBefore()以img标签为基准设置范围的起点和 setEndAfter() 以img标签为基准设置范围的终点,使用selectNode()包含img标签。 Selection对象利用DOM范围来管理选区,addRange把给定的DOM范围添加到选区。 与选区交互的方法是用document.execCommand(),这里主要是用复制功能。这个api除了复制,还可以剪贴、粘贴、添加标签、删除、修改样式。注意,为了保证Firefox的兼容性,第二个参数应始终为false。

上面的几步操作也就是clipboard.js的主要流程。它通过document.createElement(‘textarea’);使用文本框的select方法选中,setSelectionRange创建选区。除此之外,contenteditable的div标签则通过跟上文复制图片相同的方式,创建DOM范围,并添加到选区。最后执行操作的也是document.execCommand()。 下面是clipboard.js的选择操作。

function select(element) {
    var selectedText;

    if (element.nodeName === 'SELECT') {
        element.focus();

        selectedText = element.value;
    }
    else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
        var isReadOnly = element.hasAttribute('readonly');

        if (!isReadOnly) {
            element.setAttribute('readonly', '');
        }

        element.select();
        element.setSelectionRange(0, element.value.length);

        if (!isReadOnly) {
            element.removeAttribute('readonly');
        }

        selectedText = element.value;
    }
    else {
        if (element.hasAttribute('contenteditable')) {
            element.focus();
        }

        var selection = window.getSelection();
        var range = document.createRange();

        range.selectNodeContents(element);
        selection.removeAllRanges();
        selection.addRange(range);

        selectedText = selection.toString();
    }

    return selectedText;
}

当我自信满满认为这个方案已经很好,现实给了我一击。 PC微信端的富文本编辑器无法识别到通过execCommand复制的内容,猜测是微信做了过滤。 于是我查了一下w3c的规范,https://www.w3.org/TR/clipboard-apis/#copy-action

Copy the selected contents, if any, to the clipboard. Implementations should create alternate text/html and text/plain clipboard formats when content in a web page is selected.

大概意思是通过选择后执行execCommand复制的内容格式只有text/html和text/plain。

复制的图片类型是text/html,猜测PC微信的富文本编辑器是对此做了过滤,所以无法粘贴。 如果业务需求跟微信相关,那么大概可以放弃这种方式了。我没有找到把选区转换成image/png类型去复制的方法…

2. 通过canvas直接写入clipboard

    const imgURL = 'https://github.githubassets.com/images/icons/emoji/unicode/270d.png';
    const img = document.createElement('img');
    img.src = imgURL;
    img.crossOrigin="Anonymous";
    img.onload = function () {
      copyImg(img);
    }
  function copyImg(img) {
    const canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;
    const context = canvas.getContext('2d');
    context.drawImage(img, 0, 0, img.width, img.height);
    if (canvas.toBlob) {
      canvas.toBlob((blob) => {
        const clipboardItem = new ClipboardItem({[blob.type]: blob});
        navigator.clipboard.write([clipboardItem]);
      });
    }
  }
  • 此种方式是通过canvas作为中间转换,调用canvas.toBlob。有了Blob可以很方便构造ClipboardItem对象,作为clipboard.write的参数,写入到clipboard。

3.通过fetch直接写入clipboard

    const imgURL = 'https://github.githubassets.com/images/icons/emoji/unicode/270d.png';
    const text = 'Hello, world!'
    const imgRes = await fetch(imgURL);
    const imgBlob = await imgRes.blob();
    const clipboardItemImg = new ClipboardItem({[imgBlob.type]: imgBlob});
    navigator.clipboard.write([clipboardItemImg])
  • 借用fetch方法,返回Response对象,可以把图片转换成Blob。

  • 注意

    • clipboard.write和ClipboardItem 在 chrome 66版本开始支持。

    • 图片源地址需要支持跨域。

    • clipboard.write入参是个数组,像是支持多个clipboardItem,可以复制图文样子。现实是塞入多个clipboardItem实例就会报错…

        Uncaught (in promise) DOMException: Support for multiple ClipboardItems is not implemented.
      
  • 总结

    • 间接写入clipboard方式功能最强大(可以复制图文,并且可以保留样式),兼容性最好,但是微信的富文本编辑器会过滤text/html类型,在很多业务场景用不了。
    • 两种直接写入clipboard的方式简单快捷,不用操作DOM,不用创建Range、Selection对象,但兼容性不佳,只能复制图片,无法图文一起复制。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值