React组件封装 - 实现水印功能

1,124 阅读3分钟

背景:在开发移动端内部应用的时候,涉及安全问题,我们经常在企业微信或者图片上看到水印,防止信息被泄露,针对这次开发做个复盘,记录下。

效果图如下: image.png

一、实现原理

  • 1、首先用canvas绘制水印
  • 2、创建蒙层div,可以覆盖在页面上,并设置pointer-events:none属性
  • 3、将canvas绘制的水印作为背景图重复渲染在第二步创建的div上
  • 4、将第三步水印div插入容器中

二、组件封装

1、新建移动端项目,具体见构建方法

2、定义组件接收的参数,包括水印覆盖的样式和文本内容

import PropTypes from 'prop-types'
WaterMark.propTypes = {
  classNamePropTypes.string,
  txtPropTypes.string,
  restProps: PropTypes.object
}
WaterMark.defaultProps = {
  txt"watermark"
}

3、创建容器,将需要打水印的页面包含其中

<div
      className={`watermark_wrapper ${className ? `${className}` : ""}`}
      style={{ position: "relative" }}
      {...restProps}
    >
      {children}
</div>

4、canvas绘制水印

这里设置水印大小,文字样式,旋转角度等,最终返回一个canvas。

const generateWaterLog = (content) => {
    const canvas = document.createElement("canvas");
    canvas.setAttribute("width", "100px");
    canvas.setAttribute("height", "100px");
    const ctx = canvas.getContext("2d");
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.fontSize = 24;
    ctx.fillStyle = "rgba(184, 184, 184, 0.8)";
    ctx.rotate(-(Math.PI / 180) * 30);
    ctx.fillText(content, 100 / 2, 100 / 2);
    return canvas
}

5、创建蒙层将上一步绘制的水印放在上面

设置蒙层div通过绝对定位放在文本表面,将canvas转化成base64,作为背景图重复渲染在蒙层上。 这样设置会导致,页面服务点击操作,这时候要设置 pointer-events: none; 让蒙层鼠标事件失效。这样不影响实际内容的操作。

const generateWaterMark = (canvas) => {
    const bg_url = canvas.toDataURL()
    const waterMarkDiv = document.createElement("div");
    const style = `
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      z-index: 1000;
      pointer-events: none;
      background-repeat: repeat;
      background-image: url('${bg_url}')
    `;
    waterMarkDiv.setAttribute("style", style);
    waterMarkDiv.setAttribute("class""watermark-bg");
    return waterMarkDiv
}

6、渲染水印

生成了蒙层,并且渲染了水印,调用生成水印函数,获取要渲染水印的容器,并给其中插入水印div, 这里要排除下节点className为水印蒙层的div

generateTextWatermark(txt, document.body.querySelectAll(".watermark_wrapper"))

const generateTextWatermark = (content, container) => {
    const canvas = generateWaterLog(content)
    const waterMarkDiv = generateWaterMark(canvas)
    Array.from(container).forEach((node) => {
      if (!node.getElementsByClassName("watermark-bg").length) {
        node.appendChild(waterMarkDiv.cloneNode(true));
      }
    });
  };

7、调用

import { WaterMark } from "../../components"
<WaterMark>
    <div>需要添加水印的文本</div>
</WaterMark>

三、demo示例

yarn安装

yarn add demo-ui

npm安装

npm install -S demo-ui

使用

import React from "react";
import { WaterMark } from 'demo-ui'
function Demo() {
    return (
        <WaterMark>
            <div className="padding10 font-size-14 demo">
                <h2 className="text-center font-size-16">水印DEMO</h2>
                <header className="text-indent-2">
                    本协议将对用户使用本产品的行为产生法律约束力,您已承诺和保证有权利和能力订立本协议。用户开始使用本产品将视为已经接受本协议,请认真阅读并理解本协议中各种条款,包括免除和限制我们的免责条款和对用户的权利限制(未成年人审阅时应由法定监护人陪同),如果您不能接受本协议中的全部条款,请勿开始使用本产品。
                </header>
                <div>
                    <h4>一、使用账户</h4>
                    <p className="text-indent-2">
                        您必须承诺和保证: 1. 您使用本产品的行为必须合法
                        本产品将会依据本协议“修改和终止”的规定保留或终止您的账户。您必须承诺对您的登录信息保密、不被其他人获取与使用,并且对您在本账户下的所有行为负责。您必须将任何有可能触犯法律的、未授权使用或怀疑为未授权使用的行为在第一时间通知本产品。本产品不对您因未能遵守上述要求而造成的损失承担法律责任。
                    </p>
                </div>
            </div>
        </WaterMark>
    );
}
export default Demo;