跳转到内容

复制图片到剪贴板

源码

ts
import { loadImage } from '../load-image/code'
import { imageToCanvas } from '../image-to-canvas/code'
import { canvasToBlob } from '../canvas-to-blob/code'
import { copyFileToClipboard } from '../copy-file/code'

/**
 * 复制图片到剪贴板
 * @example
 * copyImageToClipboard('https://example.com/image.png')
 * copyImageToClipboard(image)
 */
export function copyImageToClipboard(url: string): Promise<void>
export function copyImageToClipboard(
  image: HTMLImageElement,
): Promise<void>
export async function copyImageToClipboard(
  urlOrImage: string | HTMLImageElement,
) {
  if (!navigator.clipboard) {
    throw new Error('Clipboard API is not supported in current browser')
  }

  const image =
    typeof urlOrImage === 'string'
      ? await loadImage(urlOrImage, { crossOrigin: 'anonymous' })
      : urlOrImage

  const canvas = imageToCanvas(image)
  const blob = await canvasToBlob(canvas)
  await copyFileToClipboard(blob)
}

源码用到的函数

loadImage

ts
export interface LoadImageOptions {
  /**
   * 是否撤销
   */
  revoke?: boolean

  /**
   * 图片宽度
   */
  width?: number

  /**
   * 图片高度
   */
  height?: number

  /**
   * 跨域
   */
  crossOrigin?: 'anonymous' | 'use-credentials' | ''
}

/**
 * 加载图片
 */
export async function loadImage(
  src: string,
  options?: LoadImageOptions,
): Promise<HTMLImageElement> {
  const { revoke = false, width, height, crossOrigin } = options || {}

  return new Promise<HTMLImageElement>((resolve, reject) => {
    const img = new Image(width, height)
    if (crossOrigin) img.crossOrigin = crossOrigin
    img.onload = () => {
      resolve(img)
      if (revoke) URL.revokeObjectURL(img.src)
    }
    img.onerror = () => {
      reject(new Error('load image error'))
      if (revoke) URL.revokeObjectURL(img.src)
    }
    img.src = src
  })
}

imageToCanvas

ts
/**
 * Image 转 Canvas
 */
export function imageToCanvas(image: HTMLImageElement) {
  const canvas = document.createElement('canvas')
  canvas.width = image.naturalWidth
  canvas.height = image.naturalHeight
  const ctx = canvas.getContext('2d')
  if (!ctx) {
    throw new Error('Canvas 2D API is not supported in current browser')
  }
  ctx.drawImage(image, 0, 0)
  return canvas
}

canvasToBlob

ts
/**
 * Canvas 转 Blob
 * @param canvas 需要转换的 HTMLCanvasElement 元素
 * @param type MIME 类型(如 'image/png', 'image/jpeg' 等)
 * @param quality 图像质量,取值范围 0-1
 * @example
 * const blob = await canvasToBlob(canvas, 'image/png', 0.8)
 */
export function canvasToBlob(
  canvas: HTMLCanvasElement,
  type?: string,
  quality?: number,
): Promise<Blob> {
  return new Promise<Blob>((resolve, reject) => {
    canvas.toBlob(
      (blob) => {
        if (blob) {
          resolve(blob)
        } else {
          reject(new Error('canvas to blob error'))
        }
      },
      type,
      quality,
    )
  })
}

copyFileToClipboard

ts
/**
 * 复制图片到剪贴板
 */
export async function copyFileToClipboard(file: File | Blob) {
  if (!navigator.clipboard) {
    throw new Error('Clipboard API is not supported in current browser')
  }
  if (!file.type) {
    throw new Error('File type is empty')
  }
  const item = new ClipboardItem({ [file.type]: file })
  await navigator.clipboard.write([item])
}