阅读 1155

前端实现网页添加水印

前言

在Web项目中,为了保护知识产权,或者后台系统快速定位到个人,页面水印的存在是非常有必要的。下面分享一种简单的前端添加页面水印的方式。

技术分析

从效果来说,希望实现的效果是在需要加载水印的盒子上覆盖上水印信息。首先想到的方案就是添加一个新的盒子,填充满想要添加水印的元素。然后将水印设置为背景图。不过后来想到,如果一个页面上很多元素想要分别添加水印,这样需要的操作太繁琐,而且会插入很多不必要的dom。所以改为使用:after伪元素的方式来实现。定义好伪元素之后,只需要在想要添加水印的dom上增加一个类名就可以了。不过两种方案首先需要实现的就是通过文字生成图片。

实现方案

1. 生成背景图

实现自定义文字内容的图片,可以通过canvas来生成一个自定义文字的画布,然后调用canvas.toDataURL()生成base64字符串,作为元素的背景图;实现代码如下

// 配置类型定义
type imgOptions = {
  // 宽度
  width: number;
  // 高度
  height: number;
  // 水印内容
  content: string;
  // 水印字体
  font: string;
  // 水印颜色
  color: string;
  // 偏转角度(deg)
  rotateDegree: number;
  // X轴偏移量
  x: number;
  // Y轴偏移量
  y: number;
};

function createImgBase(options: imgOptions): string {
  const canvas = document.createElement('canvas');
  const text = options.content;
  // 因为要实现文字交错效果,所以这里生成两倍宽度的图片
  canvas.width = options.width * 2;
  canvas.height = options.height;
  const ctx = canvas.getContext('2d');
  if (ctx) {
    // X轴阴影距离,负值表示往上,正值表示往下
    ctx.shadowOffsetX = 2;
    // Y轴阴影距离,负值表示往左,正值表示往右
    ctx.shadowOffsetY = 2;
    // 阴影的模糊程度
    ctx.shadowBlur = 2;
    ctx.font = options.font;
    ctx.fillStyle = options.color;
    ctx.rotate(options.rotateDegree);
    ctx.textAlign = 'left';
    ctx.fillText(text, options.x, options.y);
  }
  return canvas.toDataURL('image/png');
}
复制代码

2. 将背景图添加到伪元素内

根据技术分析方案,可以将生成的img设为某个类::after伪元素的背景图,这样只需要给想要添加水印的dom添加上自定义的类名,就可以给此元素添加水印了,实现方法如下

// 配置类型
type watermarkOptions = {
  // 自定义类名
  className?: string;
  // 宽度
  width?: number;
  // 高度
  height?: number;
  // 文案内容
  content?: string;
  // 字体
  font?: string;
  // 颜色
  color?: string;
  // 偏转角度
  rotate?: number;
  // 定位方式
  position?: string;
  // 顶部距离
  top?: number;
  // 左侧距离
  left?: number;
  // 层级
  zIndex?: number;
};

/**
 * 生成水印
 * @param {String} className 水印类名
 * @param {Number} width 宽度
 * @param {Number} height 高度
 * @param {String} content 内容
 * @param {String} font 字体
 * @param {String} color 自定义样式: 如字体颜色(使用RGBA)
 * @param {Number} rotate 翻转角度
 * @param {String} position 水印定位方式
 * @param {Number} top 距离顶部位置
 * @param {Number} left 距离左部位置
 * @param {Number} zIndex 水印层级
 */
function genWaterMark({
  className = 'watermarked',
  width = 340,
  height = 240,
  content = '水印',
  font = '14px PingFang SC, sans-serif',
  color = 'rgba(156, 162, 169, 0.3)',
  rotate = -14,
  position = 'absolute',
  top = 0,
  left = 0,
  zIndex = 1000,
}: watermarkOptions): void {
  const option = {
    width,
    height,
    content,
    font,
    color,
    rotateDegree: (rotate * Math.PI) / 180,
  };

  // 为了实现交错水印的效果,此处生成两张位置不同的水印 固定相对位置
  const dataUri1 = createImgBase({
    ...option,
    x: 100,
    y: 140,
  });
  const dataUri2 = createImgBase({
    ...option,
    x: 400,
    y: 340,
  });

  let defaultStyle = document.createElement('style');
  defaultStyle.innerHTML = `.${className}:after {
    content: '';
    display: block;
    width: 100%;
    height: 100%;
    ${top || top === 0 ? `top: ${top}px;` : ''}
    ${left || left === 0 ? `left: ${left}px;` : ''}
    background-repeat: repeat;
    pointer-events: none;
  }`;

  let styleEl = document.createElement('style');
  styleEl.innerHTML = `.${className}:after
  {
    ${position ? `position: ${position}` : ''};
    ${zIndex ? `z-index:${zIndex}` : ''};
    background-image: url(${dataUri1}), url(${dataUri2});
    background-size: ${option.width * 2}px ${option.height}px;
  }`;
  document.head.appendChild(defaultStyle);
  document.head.appendChild(styleEl);
}


复制代码

3. 调用方法

  • 新建一个HTML页面,引入js
<style>
    body {
      margin: 0;
    }
    * {
      box-sizing: border-box;
    }
    .main {
      height: 100vh;
      width: 100vw;
      padding: 15px;
      display: flex;
      justify-content: space-between;
    }

    .main .left,
    .main .right {
      position: relative;
      height: 100%;
      width: 49%;
      border: 1px solid #66ccff;
    }
</style>
<body>
    <script src="./water-mark.js"></script>
    <div class="main">
        <div class="left"></div>
        <div class="right"></div>
    </div>
</body>
复制代码

效果如下 image.png

  • 调用方法
const option = {
    content: '测试水印',
    className: 'watermarked',
};
genWaterMark(option);
复制代码
  • 首先给.left添加类名试验效果
  <div class="main">
    <div class="left watermarked">
    </div>
    <div class="right"></div>
  </div>
复制代码

image.png

  • 再看下同时给.right添加水印的效果
  <div class="main">
    <div class="left watermarked">
    </div>
    <div class="right watermarked"></div>
  </div>
复制代码

image.png

总结

至此,前端生成并添加水印的方案基本完成。不过此方案的安全性是很脆弱的,稍微懂点前端知识的人都知道可以通过控制台修改css来隐藏水印,这样就起不到保护效果了。因此,需要设计一个方案,来禁止用户通过控制台隐藏or删除水印。水印防删解决方案

文章分类
前端
文章标签