基于react实现页面添加水印

1,673 阅读2分钟

一、 需求背景

为了防止内部数据泄露,拍照等等,预览文档,添加水印功能,可以添加登录人姓名,公司名称,公司logo等信息。

二、 实现方式

框架是用react实现水印相关功能,在页面内容区域添加一个盒子,根据页面的大小,水印间距参数计算水印的行数和列数。

特点:

1.支持水印反删除,监视 DOM 的变动,水印不可被删除、且属性不可被修改,监听DOMNodeRemoved,DOMSubtreeModified方法。

2. 动态水印设置:在浏览器地址后面拼接水印参数,根window.location.search获取水印信息,重新设置水印参数。

3. 移动端和pc端水印保持一致,实现思路,定义一个基准,根据页面的宽度和基准页面的宽度得到一个比值,去动态设置水印的字体,水印间距等相关信息。

4. 支持图片水印,换行水印,单个水印,平铺水印,动态设置水印,非常灵活。

具体详细可参考永中文档在线预览功能www.yozodcs.com/page/exampl…

微信图片_20220114100643.png

三 、效果图如下

pc端效果图:

微信图片_20220114095723.png

移动端效果图:

微信图片_20220114095417.jpg

四、具体实现

水印参数

// 水印参数
export interface WaterMark {
  watermark_txt: string;
  watermark_x: number;
  watermark_y: number;
  watermark_x_extra: number;
  watermark_y_extra: number;
  watermark_fontSize: number;
  watermark_tile: boolean;
  watermark_angle: number;
  watermark_alpha: number;
  watermark_family: string;
  watermark_fontColor: string;
  watermark_picPath: string;
  watermark_picWidth: number;
  watermark_picHeight: number;
}

根据页面的大小,水印间距参数计算水印的行数和列数。

/**
 * 水印处理相关
 * @param wrapperW 水印展示包裹区宽度
 * @param wrapperH 水印展示包裹区高度
 * @param watermarkW 旋转(仿射)变换后水印的宽度
 * @param watermarkH 旋转(仿射)变换后水印的高度
 * @return pos 各水印定位位置组成的数组
 */
export function handleWatermark(
  wrapperW: number,
  wrapperH: number,
  watermarkW: number,
  watermarkH: number,
  watermarkInfo: WaterMark,
) {
  const { watermark_y, watermark_x, watermark_y_extra, watermark_x_extra } =
    watermarkInfo;
  // 计算每行/列需要排布的数目
  const nRows = Math.ceil(wrapperH / (watermarkH + watermark_y_extra));
  const nCols = Math.ceil(wrapperW / (watermarkW + watermark_x_extra));
  // 构建排布的各水印top/left
  let pos = [],
    top: number,
    left: number;
  // console.log(nRows, nCols);
  for (let i = 0; i < nRows; i++) {
    top = Math.round(watermark_y + i * (watermarkH + watermark_y_extra));
    for (let j = 0; j < nCols; j++) {
      // 偶数行保证参差感
      if (i % 2 === 1) {
        left = watermark_x + 50 + j * (watermarkW + watermark_x_extra);
      } else {
        left = watermark_x + j * (watermarkW + watermark_x_extra);
      }
      pos.push({
        top: top,
        left: left,
      });
    }
  }
  return pos;
}

水印不可篡改具体实现:监听事件shouldRerender,DOMSubtreeModified,页面dom元素是否改变或者删除,重新加载水印组件

 useEffect(() => {
    const wmWrapperEl = contentRef?.current?.parentNode;
    const contentEl = contentRef?.current;
    function shouldRerender() {
      eventBus.emit('shouldRerender');
    }
    //水印不可删除
    wmWrapperEl?.addEventListener('DOMNodeRemoved', shouldRerender);
    //水印不可修改
    contentEl?.addEventListener('DOMSubtreeModified', shouldRerender);
    return () => {
      wmWrapperEl?.removeEventListener('DOMNodeRemoved', shouldRerender);
      contentEl?.removeEventListener('DOMSubtreeModified', shouldRerender);
    };
    }, [contentRef]);

五、结尾

本文仅介绍了水印的简单实现,也可以利用canvas实现水印,canvas元素其实就是一个画布,我们可以很方便地绘制一些文字、线条、图形等,它也可以将一个img标签里渲染的图片画在画布上。