前端实现水印的方法

163 阅读2分钟

最近在学习一些工具的实现方案,恰好认真研究了下前端实现水印的方式和原理,下面让我们一起看看水印究竟是怎么实现的吧
其实目前基于水印的实现方式有很多种,包括但不限于

  • 后端基于原图生成水印图片
  • 前端基于DOM实现水印效果
  • 前端基于Canvas实现水印效果

今天我们着重讲解下基于Canvas实现水印的原理
核心步骤:

  • 使用canvas生成画布,并通过canvas.toDataURL("image/png")生成水印之后的图片
  • 创建一个新的div用来append已生成的canvas

原理是非常简单的,我们也可以通过不同配置来灵活配置水印
完整代码如下:

// waterMarkUtils.ts
const setWaterMark = (str: string, options?) => {
  const setOptions = {
    fillStyle: options?.fillStyle || "red",
    gradient: options?.gradient || [],
    rotate:
      options?.rotate === 0
        ? options.rotate
        : options?.rotate || (-20 * Math.PI) / 180,
    font: options?.rotate || "12px Vendana",
    textBaseline: options?.textBaseline || "middle",
    width: options?.width || document.documentElement.clientWidth,
    height: options?.height || document.documentElement.clientHeight
  };
  const id = "1.23452384164";
  if (document.getElementById(id) !== null) {
    document.body.removeChild(<HTMLElement>document.getElementById(id));
  }
  const can = document.createElement("canvas");
  can.width = 200;
  can.height = 130;
  const cans = <CanvasRenderingContext2D>can.getContext("2d");
  cans.rotate(setOptions.rotate);
  cans.font = setOptions.font;
  cans.textBaseline = setOptions.textBaseline;
  // cans.fillText(str, can.width / 5, can.height / 2);
  // 设置线型值
  if (setOptions?.gradient?.length > 0) {
    setOptions.gradient.forEach(element => {
      const grd = cans.createLinearGradient(0, 0, 170, 0);
      grd.addColorStop(element.value, element.color);
      cans.fillStyle = grd;
      cans.fillText(str, can.width / 5, can.height / 2);
    });
  } else {
    // 画布字体的颜色
    cans.fillStyle = setOptions.fillStyle;
    cans.fillText(str, can.width / 5, can.height / 2);
  }
  const div = document.createElement("div");
  div.id = id;
  div.style.pointerEvents = "none";
  div.style.top = "0px";
  div.style.left = "0px";
  div.style.position = "fixed";
  div.style.zIndex = "10000000";
  div.style.width = `${setOptions.width}px`;
  div.style.height = `${setOptions.height}px`;
  div.style.background = `url(${can.toDataURL("image/png")}) left top repeat`;
  document.body.appendChild(div);
  return id;
};
const watermark = {
  // 设置水印
  set: (str: string, options = {}) => {
    let id = setWaterMark(str, options);
    if (document.getElementById(id) === null) id = setWaterMark(str, options);
  },
  // 删除水印
  delWatermark: () => {
    const id = setWaterMark("");
    if (document.getElementById(id) !== null)
      document.body.removeChild(<HTMLElement>document.getElementById(id));
  }
};
export default watermark;

示例:

setWaterMark('水印勿动', {
          fillStyle:#007fff
          gradient: [
            { value: 0, color: 'magenta' },
            { value: 0.5, color: 'red' },
            { value: 1.0, color: 'green' }
          ]
 })

水印配置详解:

配置项配置项值设置
fillStyle填充样式
gradient设置渐变样式,使其可以在两个颜色或多个指定的颜色之间显示平稳过渡,
gradient: [
            { value: 0, color: 'magenta' },
            { value: 0.5, color: 'red' },
            { value: 1.0, color: 'green' }
          ]
rotate设置水印的旋转方向
font设置水印的字体
textBaseline设置字体的基准线,属性值可以设置为:alphabetic/top/hanging/middle/ideographic/bottom
width设置每个水印的宽度
设置值每个水印的高度

看下最终的成效吧:
image.png