通过canvas给图片添加水印

138 阅读2分钟

实现功能:通过canvas给图片添加水印

web后台项目

/**
 * 图片水印
 */
import React, { useEffect, useRef, useState } from 'react'
import ImageBox from '@apps/components/src/web/ImageBox'

interface Props {
  watermarkText?: string
  imageUrl: string
  imgWidth: number
  imgHeight: number
}
function ImageWithWatermark(props: Props) {
  const { watermarkText = '仅***平台备案使用', imageUrl, imgWidth, imgHeight } = props
  const canvasRef = useRef(null)
  const [imageSrc, setImageSrc] = useState<any>('')

  useEffect(() => {
    const canvas = canvasRef.current
    const ctx = canvas.getContext('2d')

    let img = new Image()
    img.crossOrigin = 'Anonymous' // 添加 crossOrigin 属性用于图片下载
    img.src = imageUrl
    img.onload = function () {
      const radio = String(img.width > 1000 ? img.width / 500 : 1)?.split('.')[0] || 1
      const currentWidth = String(img.width / Number(radio))?.split('.')[0] || 1
      const currentHeight = String(img.height / Number(radio))?.split('.')[0] || 1

      // 初始化画布尺寸
      canvas.width = currentWidth
      canvas.height = currentHeight

      // 计算缩放比例
      let scaleWidth = canvas.width / img.width
      let scaleHeight = canvas.height / img.height
      let scale = Math.max(scaleWidth, scaleHeight)

      // 应用缩放
      let newWidth = img.width * scale
      let newHeight = img.height * scale

      // 计算绘制位置,使图片居中
      let posX = (canvas.width - newWidth) / 2
      let posY = (canvas.height - newHeight) / 2

      // 清除之前的绘制
      ctx.clearRect(0, 0, canvas.width, canvas.height)

      // 先绘制图片
      ctx.drawImage(img, posX, posY, newWidth, newHeight)

      // 设置水印样式
      ctx.font = 'italic bold 15px Arial' // 使用斜体和加粗
      ctx.fillStyle = 'rgba(128, 128, 128, 0.9)' // 半透明灰色
      ctx.textAlign = 'center'
      ctx.textBaseline = 'middle'
      ctx.globalAlpha = 0.9 // 设置透明度,0.5意味着50%不透明

      // 水印之间的间隔
      const spacing = 140 // 可以根据需要调整
      const textWidth = ctx.measureText(watermarkText).width
      const textHeight = parseInt(ctx.font.split(' ')[0], 10) // 获取字体高度

      // 绘制多个水印
      for (let x = -canvas.width; x <= 2 * canvas.width; x += spacing) {
        for (let y = -canvas.height; y <= 2 * canvas.height; y += spacing) {
          ctx.save() // 保存当前状态
          ctx.translate(x + canvas.width / 4, y + canvas.height / 4)
          ctx.rotate(-Math.PI / 6) // 旋转45度
          ctx.fillText(watermarkText, 0, 0)
          ctx.restore() // 恢复状态
        }
      }

      // 将 Canvas 转换为 Data URL
      const dataURL = canvas.toDataURL('image/png')
      setImageSrc(dataURL)
    }
  }, [imageUrl, watermarkText])

  return (
    <>
      {imageSrc ? (
        <ImageBox src={imageSrc} preview width={imgWidth} height={imgHeight} style={{ objectFit: 'cover' }} />
      ) : (
        <canvas className={'canvas'} ref={canvasRef} />
      )}
    </>
  )
}

export default ImageWithWatermark


Taro小程序

import React, { useEffect, useState, useRef } from 'react';
import { View, Canvas } from '@tarojs/components';
import Taro from '@tarojs/taro';

const ImageWithWatermark = (props) => {
	const { imageUrl, watermarkText = '仅****平台备案使用', canvasId, imgWidth = 300, imgHeight=300 } = props
  const canvasNodeRef = useRef(null);
  const [image, setImage] = useState<any>(null);

  useEffect(() => {
    // 加载图片
    Taro.getImageInfo({
      src: imageUrl,
      success: res => {
        console.log('图片加载成功', res);
        setImage(res.path);
      }
    });
  }, []);

  useEffect(() => {
    if (image) {
      drawImageWithWatermark(image);
    }
  }, [image]);

  const drawImageWithWatermark = (info) => {
    let ctx = Taro.createCanvasContext(canvasId);

    // 绘制背景图片
    ctx.drawImage(info, 0, 0, imgWidth, imgHeight);

    // 设置水印样式
    ctx.font = "italic bold 15px Arial"; // 使用斜体和加粗
    ctx.fillStyle = "rgba(128, 128, 128, 0.9)"; // 半透明灰色
    ctx.setTextAlign("center");
    ctx.setTextBaseline("middle");
    ctx.globalAlpha = 0.9; // 设置透明度

    // 水印之间的间隔
    const spacing = 140; // 可以根据需要调整

    // 绘制多个水印
    for (let x = -imgWidth; x <= 2 * imgWidth; x += spacing) {
      for (let y = -imgHeight; y <= 2 * imgHeight; y += spacing) {
        ctx.save(); // 保存当前状态
        ctx.translate(x + imgWidth / 4, y + imgHeight / 4);
        ctx.rotate(-Math.PI / 6); // 旋转角度
        ctx.fillText(watermarkText, 0, 0);
        ctx.restore(); // 恢复状态
      }
    }

    ctx.draw(false, () => {
      console.log('绘图完成');
    });
  };

  return (
    <View>
      <Canvas
        canvasId={canvasId}
        style={{ width: imgWidth, height: imgHeight }}
        ref={canvasNodeRef}
      />
    </View>
  );
};

export default ImageWithWatermark;