Vue3 useImageCopyToClipboard 之实现图片复制到剪贴板功能

1,011 阅读2分钟

目前,一共有三种方法可以实现剪贴板操作

  • Document.execCommand()方法
  • 异步的 Clipboard API
  • copy事件和paste事件

Document.execCommand() 方法

Document.execCommand()是操作剪贴板的传统方法,支持复制、剪切和粘贴这三个操作。

  • document.execCommand('copy')(复制)
  • document.execCommand('cut')(剪切)
  • document.execCommand('paste')(粘贴)
import { ref, readonly } from 'vue';

export function useClipboard() {
  const copiedText = ref('');
  const isCopied = ref(false);

  const copyToClipboard = (text) => {
    const textArea = document.createElement('textarea');
    textArea.value = text;

    document.body.appendChild(textArea);
    textArea.select();

    if (document.execCommand('copy')) {
      isCopied.value = true;
      copiedText.value = text;
    } else {
      isCopied.value = false;
    }

    document.body.removeChild(textArea);
  };

  const clearCopiedText = () => {
    isCopied.value = false;
    copiedText.value = '';
  };

  return {
    copiedText: readonly(copiedText),
    isCopied: readonly(isCopied),
    copyToClipboard,
    clearCopiedText,
  };
}

Document.execCommand() 是同步操作,如果复制/粘贴大量数据,页面会出现卡顿。有些浏览器还会跳出提示框,要求用户许可,这时在用户做出选择前,页面会失去响应。

Clipboard API

  • Clipboard API 所有操作都是异步的,返回 Promise 对象,不会造成页面卡顿。
  • Chrome 浏览器规定,只有 HTTPS 协议的页面才能使用这个 API。不过,开发环境(localhost)允许使用非加密协议。
  • 调用时需要明确获得用户的许可。权限的具体实现使用了 Permissions API,跟剪贴板相关的有两个权限:clipboard-write(写权限)和clipboard-read(读权限)。"写权限"自动授予脚本,而"读权限"必须用户明确同意给予。也就是说,写入剪贴板,脚本可以自动完成,但是读取剪贴板时,浏览器会弹出一个对话框,询问用户是否同意读取。
  • Clipboard.write()方法用于将任意数据写入剪贴板,可以是文本数据,也可以是二进制数据。

使用 canvas.toBlob 方法将画布内容转换为 Blob 对象,使用 navigator.clipboard.write(data) 尝试将数据写入剪贴板。

import { ref } from 'vue';

export function useImageClipboard() {
  const copiedImage = ref(null);
  const isCopied = ref(false);

  const copyImageToClipboard = (imageSrc) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.src = imageSrc;

    img.onload = () => {
      const canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = img.height;

      const context = canvas.getContext('2d');
      context.drawImage(img, 0, 0);

      canvas.toBlob((blob) => {
        const data = [new ClipboardItem({ 'image/png': blob })];

        navigator.clipboard
          .write(data)
          .then(() => {
            isCopied.value = true;
            copiedImage.value = imageSrc;
          })
          .catch((error) => {
            console.error('复制失败:', error);
            isCopied.value = false;
          });
      }, 'image/png');
    };
  };

  const clearCopiedImage = () => {
    isCopied.value = false;
    copiedImage.value = null;
  };

  return {
    copiedImage,
    isCopied,
    copyImageToClipboard,
    clearCopiedImage,
  };
}

使用

<template>
  <div>
    <button @click="copyImage">复制图片</button>
    <div v-if="isCopied">
      <img :src="copiedImage" alt="已复制的图片" />
      <button @click="clearCopiedImage">清除</button>
    </div>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { useImageClipboard } from "../hooks/useImageClipboard.js";

const { copiedImage, isCopied, copyImageToClipboard, clearCopiedImage } =
  useImageClipboard();

const imageSrc = ref(
  "https://cdn.pixabay.com/photo/2023/10/20/20/53/pears-8330221_1280.jpg"
);

const copyImage = () => {
  copyImageToClipboard(imageSrc.value);
};
</script>

Oct-26-2023 20-55-23.gif

copy事件和paste事件

用户向剪贴板放入数据时,将触发copy事件。用户使用剪贴板数据,进行粘贴操作时,会触发paste事件。

参考文档:www.ruanyifeng.com/blog/2021/0…