Front-end implementation of watermark effect

1. Problem background

In order to prevent information leakage or infringement of intellectual property rights, in the web world, it is very necessary to add watermarks to pages and pictures. The addition of watermarks can be divided into two categories according to the environment, front-end browser environment addition and back - end To add a service environment, briefly compare the characteristics of these two methods:

Front-end browser watermarking:

  • Reduce the pressure on the server and respond quickly
  • The safety factor is low. For those who have a certain front-end knowledge, they can skip the watermark and obtain the source file through various operations.
  • Applicable scenarios: resources are not bound to a single user, but a resource, and multiple users view it. It is necessary to add a user-specific watermark when each user views it. It is mostly used for some confidential documents or displaying confidential information. page, the purpose of the watermark is to trace the responsible person when the document is leaked. The backend server adds the watermark:
  • When encountering large files with dense watermarks or complex watermarks, it takes up server memory and computation, and the request time is too long
  • High security, unable to obtain source files before watermarking
  • Applicable scenario: The resource is unique to a certain user. An original resource only needs to be processed once, and it does not need to be processed again after it is stored. The purpose of the watermark is to mark the owner of the resource. Here we discuss the addition of the front-end browser environment.

2. Realize the effect

 

3. Implementation plan

1. Duplicate dom element override implementation

The first solution that comes to mind is to cover a div box with position:fixed on the page, set the transparency of the box to be low, set pointer-events: none; style to achieve click penetration, and generate a small watermark div through js loop in this box , each watermark div displays a watermark content to be displayed, simple implementation

This solution needs to loop and create multiple dom elements in js, which is neither elegant nor affects performance, so it is considered whether or not to generate so many elements.

2. The canvas draws the background image and MutationObserver listens

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>水印</title>
</head>

<body>
    <div id="content">
        <p>这是一段内容</p>
    </div>

    <script>

        // 定义水印函数
        function addWatermark({
            container = document.body, // 水印添加到的容器,默认为 body
            width = "300px", // 水印 canvas 的宽度
            height = "250px", // 水印 canvas 的高度
            textAlign = "center", // 水印文字的对齐方式
            textBaseline = "middle", // 水印文字的基线
            font = "16px Microsoft Yahei", // 水印文字的字体
            fillStyle = "rgba(184, 184, 184, 0.6)", // 水印文字的填充样式
            content = "水印", // 水印文字的内容
            rotate = "45", // 水印文字的旋转角度
            zIndex = 10000, // 水印的 z-index 值
        }) {
            // 生成水印 canvas
            const canvas = document.createElement("canvas");
            canvas.setAttribute("width", width);
            canvas.setAttribute("height", height);
            const ctx = canvas.getContext("2d");
            ctx.textAlign = textAlign;
            ctx.textBaseline = textBaseline;
            ctx.font = font;
            ctx.fillStyle = fillStyle;
            ctx.rotate((Math.PI / 180) * rotate);
            ctx.fillText(content, parseFloat(width) / 2, parseFloat(height) / 2);

            // 将 canvas 转换为 base64 URL
            const base64Url = canvas.toDataURL();
            const __wm = document.querySelector('.__wm');
            const watermarkDiv = __wm || document.createElement("div");
            const styleStr = `
                        position: fixed;
                        top: -70px;
                        left: 0;
                        bottom: 0;
                        right: 0;
                        width: 100%;
                        height: 100%;
                        z-index: ${zIndex};
                        pointer-events: none;
                        background-repeat: repeat;
                        background-image: url('${base64Url}')
                    `;
            watermarkDiv.setAttribute("style", styleStr);
            watermarkDiv.classList.add("__wm"); 
           //则创建一个 div 并设置样式和类名

            if (!__wm) {
                container.style.position = 'relative';
                container.insertBefore(watermarkDiv, container.firstChild);
            }
            
        }



        // 调用 addWatermark 函数添加水印
        addWatermark({
            container: document.getElementById("content"),
            width: "300px",
            height: "200px",
            textAlign: "center",
            textBaseline: "middle",
            font: "16px Microsoft Yahei",
            fillStyle: "rgba(184, 184, 184, 0.3 )",
            content: "水印 6512",
            rotate: "30",
            zIndex: 10000,
        });
    </script>
</body>

</html>

There is a common problem in the method, because the DOM elements generated by the front end are overlaid on the page, for those with some knowledge of the front end, you can find the element where the watermark is located in the developer tools, and delete the entire element to delete the page The purpose of the watermark on the above, for this problem, I thought of a very stupid way: set a timer, check every few seconds whether our watermark element is still there, whether it has been modified, and execute it again if there is a change Method to override the watermark. I saw another solution on the Internet: use MutationObserver

MutationObserver is a change observer, which can be literally understood to be used to observe node changes. The Mutation Observer API is used to monitor DOM changes. This API can be notified of any changes in the DOM, such as the increase or decrease of child nodes, changes in attributes, and changes in text content.

However, MutationObserver can only monitor changes such as attribute changes and child node changes. There is no way to monitor itself being deleted. Here, the requirements can be met by monitoring the parent node. Implementation of monitoring code:

 // 监听容器变化,当容器发生变化时重新调用 addWatermark 函数
              const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
                if (MutationObserver) {
                    let mo = new MutationObserver(function () {
                        const __wm = document.querySelector('.__wm');
                        // 只在__wm元素变动才重新调用__canvasWM
                        if ((__wm && __wm.getAttribute('style') !== styleStr) || !__wm) {
                            // 避免一直触发
                            mo.disconnect();
                            mo = null;
                            console.log('1');
                            addWatermark({
                                container: document.getElementById("content"),
                                width: "300px",
                                height: "200px",
                                textAlign: "center",
                                textBaseline: "middle",
                                font: "16px Microsoft Yahei",
                                fillStyle: "rgba(184, 184, 184, 0.3 )",
                                content: "孙淼 6512",
                                rotate: "30",
                                zIndex: 10000,
                            });
                        }
                    });

                    mo.observe(container, {
                        attributes: true,
                        subtree: true,
                        childList: true
                    });
                }

Final code:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>水印</title>
</head>

<body>
    <div id="content">
        <p>这是一段内容</p>
    </div>

    <script>

        // 定义水印函数
        function addWatermark({
            container = document.body, // 水印添加到的容器,默认为 body
            width = "300px", // 水印 canvas 的宽度
            height = "250px", // 水印 canvas 的高度
            textAlign = "center", // 水印文字的对齐方式
            textBaseline = "middle", // 水印文字的基线
            font = "16px Microsoft Yahei", // 水印文字的字体
            fillStyle = "rgba(184, 184, 184, 0.6)", // 水印文字的填充样式
            content = "水印", // 水印文字的内容
            rotate = "45", // 水印文字的旋转角度
            zIndex = 10000, // 水印的 z-index 值
        }) {
            // 生成水印 canvas
            const canvas = document.createElement("canvas");
            canvas.setAttribute("width", width);
            canvas.setAttribute("height", height);
            const ctx = canvas.getContext("2d");
            ctx.textAlign = textAlign;
            ctx.textBaseline = textBaseline;
            ctx.font = font;
            ctx.fillStyle = fillStyle;
            ctx.rotate((Math.PI / 180) * rotate);
            ctx.fillText(content, parseFloat(width) / 2, parseFloat(height) / 2);

            // 将 canvas 转换为 base64 URL
            const base64Url = canvas.toDataURL();
            const __wm = document.querySelector('.__wm');
            const watermarkDiv = __wm || document.createElement("div");
            const styleStr = `
                        position: fixed;
                        top: -70px;
                        left: 0;
                        bottom: 0;
                        right: 0;
                        width: 100%;
                        height: 100%;
                        z-index: ${zIndex};
                        pointer-events: none;
                        background-repeat: repeat;
                        background-image: url('${base64Url}')
                    `;
            watermarkDiv.setAttribute("style", styleStr);
            watermarkDiv.classList.add("__wm"); 
           //则创建一个 div 并设置样式和类名

            if (!__wm) {
                container.style.position = 'relative';
                container.insertBefore(watermarkDiv, container.firstChild);
            }
              // 监听容器变化,当容器发生变化时重新调用 addWatermark 函数
              const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
                if (MutationObserver) {
                    let mo = new MutationObserver(function () {
                        const __wm = document.querySelector('.__wm');
                        // 只在__wm元素变动才重新调用__canvasWM
                        if ((__wm && __wm.getAttribute('style') !== styleStr) || !__wm) {
                            // 避免一直触发
                            mo.disconnect();
                            mo = null;
                            console.log('1');
                            addWatermark({
                                container: document.getElementById("content"),
                                width: "300px",
                                height: "200px",
                                textAlign: "center",
                                textBaseline: "middle",
                                font: "16px Microsoft Yahei",
                                fillStyle: "rgba(184, 184, 184, 0.3 )",
                                content: "孙淼 6512",
                                rotate: "30",
                                zIndex: 10000,
                            });
                        }
                    });

                    mo.observe(container, {
                        attributes: true,
                        subtree: true,
                        childList: true
                    });
                }
        }



        // 调用 addWatermark 函数添加水印
        addWatermark({
            container: document.getElementById("content"),
            width: "300px",
            height: "200px",
            textAlign: "center",
            textBaseline: "middle",
            font: "16px Microsoft Yahei",
            fillStyle: "rgba(184, 184, 184, 0.3 )",
            content: "水印 6512",
            rotate: "30",
            zIndex: 10000,
        });
    </script>
</body>

</html>

Guess you like

Origin blog.csdn.net/lovecoding1/article/details/130272158