• 水印项目的实现以及两种实现方案的选优


    水印项目我们提出了两种解决方案

    一、用shadow dom实现

    1、基本思路

    通过 attachShadow 这个方法生成一个shadow root 即shadow的根节点,然后在这个根节点下面通过循环语句添加水印,利用position为absolute进行排版,使其铺满容器

     show me the code:

    (function (root, factory) {
        if (typeof define === 'function' && define.amd) {
            /*AMD. Register as an anonymous module.
            *define([], factory); */
            define([], factory());
        } else if (typeof module === 'object' && module.exports) {
            /*Node. Does not work with strict Commonjs, but
            // only Commonjs-like environments that support module.exports,
            // like Node.*/
            module.exports = factory();
    
        } else {
            /*Browser globals (root is window)*/
            root['watermark'] = factory();
        }
    }(this, function () {
    
        /*Just return a value to define the module export.*/
        let watermark = {};
    
        /*加载水印*/
        let loadMark = function () {
            let defaultSettings = {
                watermark_id: 'wm_div_id',          //水印总体的id
                watermark_prefix: 'mask_div_id',    //小水印的id前缀
                watermark_txt: "测试水印",             //水印的内容
                watermark_x: 20,                     //水印起始位置x轴坐标
                watermark_y: 40,                     //水印起始位置Y轴坐标
                watermark_rows: 0,                   //水印行数
                watermark_cols: 0,                   //水印列数
                watermark_x_space: 100,              //水印x轴间隔
                watermark_y_space: 50,               //水印y轴间隔
                watermark_font: '微软雅黑',           //水印字体
                watermark_color: 'black',            //水印字体颜色
                watermark_fontsize: '18px',          //水印字体大小
                watermark_alpha: 0.15,               //水印透明度,要求设置在大于等于0.005
                watermark_ 100,                //水印宽度
                watermark_height: 100,               //水印长度
                watermark_angle: 15,                 //水印倾斜度数
                watermark_parent_selector: null,  //水印插件挂载的父元素选取器,不输入则默认挂在body上
                need_adapt_screen: false,     // 是否根据屏幕的分辨率等比变化每个水印的宽度和字体大小
                watermark_width_proportion: 15, // 每个水印宽度自适应屏幕变化的等比放大或缩小的值
                watermark_fontsize_proportion: 95, // 每个水印字体大小自适应屏幕变化的等比放大或缩小的值
            };
            let watermark_parent_node = null  //水印插件挂载的父元素element,不输入则默认挂在body上
    
            let setting = arguments[0] || {};
            /*采用配置项替换默认值,作用类似jquery.extend*/
            if (arguments.length === 1 && typeof arguments[0] === "object") {
                for (let key in setting) {
                    if (setting[key] && defaultSettings[key] && setting[key] === defaultSettings[key]) continue;
                    /*veronic: resolution of watermark_angle=0 not in force*/
                    else if (setting[key] || setting[key] === 0) defaultSettings[key] = setting[key];
                }
            }
    
            /* 设置水印的容器 */
            if (defaultSettings.watermark_parent_selector) {
                watermark_parent_node = document.querySelector(defaultSettings.watermark_parent_selector)
            } else {
                watermark_parent_node = document.body
            }
    
            /*如果元素存在则移除*/
            let watermark_element = document.getElementById(defaultSettings.watermark_id);
            if (watermark_element) {
                let _parentElement = watermark_element.parentNode;
                if (_parentElement) {
                    _parentElement.removeChild(watermark_element);
                }
            }
            
            /*获取水印的起始位置*/
            let page_offsetTop = 0;
            let page_offsetLeft = 0;
            page_offsetTop = watermark_parent_node.offsetTop || 0;
            page_offsetLeft = watermark_parent_node.offsetLeft || 0;
            page_width = watermark_parent_node.offsetWidth - defaultSettings.watermark_width / 2 || 0;
            page_height = (Math.max(watermark_parent_node.offsetHeight, watermark_parent_node.scrollHeight) - defaultSettings.watermark_height / 2) || 0;
            defaultSettings.watermark_x = defaultSettings.watermark_x + page_offsetLeft;
            defaultSettings.watermark_y = defaultSettings.watermark_y + page_offsetTop;
    
            /*创建水印外壳div*/
            let otdiv = document.getElementById(defaultSettings.watermark_id);
            let shadowRoot = null;
    
            if (!otdiv) {
                otdiv = document.createElement('div');
    
                /*创建shadow dom*/
                otdiv.id = defaultSettings.watermark_id;
                otdiv.style.pointerEvents = "none";
    
                /*判断浏览器是否支持attachShadow方法*/
                if (typeof otdiv.attachShadow === 'function') {
                    shadowRoot = otdiv.attachShadow({mode: 'open'});
                } else if (typeof otdiv.createShadowRoot === 'function') {
                    shadowRoot = otdiv.createShadowRoot();
                } else {
                    shadowRoot = otdiv;
                }
    
                watermark_parent_node.appendChild(otdiv)
    
            } else if (otdiv.shadowRoot) {
                shadowRoot = otdiv.shadowRoot;
            }
    
            // shadow内加个容器
            let shadowOutDiv = document.createElement('div')
            shadowOutDiv.id = 'shadowContainer'
            shadowRoot.appendChild(shadowOutDiv)
    
    
            /*如果将水印列数设置为0,或水印列数设置过大,超过页面最大宽度,则重新计算水印列数和水印x轴间隔*/
            if (defaultSettings.watermark_cols == 0 || (parseInt(defaultSettings.watermark_x + defaultSettings.watermark_width * defaultSettings.watermark_cols + defaultSettings.watermark_x_space * (defaultSettings.watermark_cols - 1)) > page_width)) {
                defaultSettings.watermark_cols = parseInt((page_width - defaultSettings.watermark_x + page_offsetLeft) / (defaultSettings.watermark_width + defaultSettings.watermark_x_space));
                defaultSettings.watermark_x_space = parseInt((page_width - defaultSettings.watermark_x + page_offsetLeft - defaultSettings.watermark_width * defaultSettings.watermark_cols) / (defaultSettings.watermark_cols - 1));
            }
            /*如果将水印行数设置为0,或水印行数设置过大,超过页面最大长度,则重新计算水印行数和水印y轴间隔*/
            if (defaultSettings.watermark_rows == 0 || (parseInt(defaultSettings.watermark_y + defaultSettings.watermark_height * defaultSettings.watermark_rows + defaultSettings.watermark_y_space * (defaultSettings.watermark_rows - 1)) > page_height)) {
                defaultSettings.watermark_rows = parseInt((page_height - defaultSettings.watermark_y + page_offsetTop) / (defaultSettings.watermark_height + defaultSettings.watermark_y_space));
                defaultSettings.watermark_y_space = parseInt(((page_height - defaultSettings.watermark_y + page_offsetTop) - defaultSettings.watermark_height * defaultSettings.watermark_rows) / (defaultSettings.watermark_rows - 1));
            }
    
            let x;
            let y;
            for (let i = 0; i < defaultSettings.watermark_rows; i++) {
                y = defaultSettings.watermark_y + (defaultSettings.watermark_y_space + defaultSettings.watermark_height) * i;
                for (let j = 0; j < defaultSettings.watermark_cols; j++) {
                    x = defaultSettings.watermark_x + (defaultSettings.watermark_width + defaultSettings.watermark_x_space) * j;
    
                    let mask_div = document.createElement('div');
                    let oText = document.createTextNode(defaultSettings.watermark_txt);
                    mask_div.appendChild(oText);
                    /*设置水印相关属性start*/
                    mask_div.id = defaultSettings.watermark_prefix + i + j;
                    /*设置水印div倾斜显示*/
                    mask_div.style.webkitTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
                    mask_div.style.MozTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
                    mask_div.style.msTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
                    mask_div.style.OTransform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
                    mask_div.style.transform = "rotate(-" + defaultSettings.watermark_angle + "deg)";
                    mask_div.style.visibility = "";
                    mask_div.style.position = "absolute";
                    /*选不中*/
                    mask_div.style.left = x + 'px';
                    mask_div.style.top = y + 'px';
                    mask_div.style.overflow = "hidden";
                    mask_div.style.zIndex = "9999999";
                    mask_div.style.opacity = defaultSettings.watermark_alpha;
                    mask_div.style.fontSize = defaultSettings.watermark_fontsize;
                    mask_div.style.fontFamily = defaultSettings.watermark_font;
                    mask_div.style.color = defaultSettings.watermark_color;
                    mask_div.style.textAlign = "center";
                    mask_div.style.width = defaultSettings.watermark_width + 'px';
                    mask_div.style.height = defaultSettings.watermark_height + 'px';
                    mask_div.style.display = "block";
                    mask_div.style['-ms-user-select'] = "none";
                    /*设置水印相关属性end*/
                    shadowOutDiv.appendChild(mask_div);
    
                }
            }
        };
    
        /*初始化水印,添加load和resize事件*/
        watermark.init = function (settings) {
            window.addEventListener('load', function () {
                loadMark(settings);
            });
            window.addEventListener('resize', function () {
                loadMark(settings);
            });
            window.addEventListener('DOMContentLoaded', function () {
                loadMark(settings);
            });
    
        };
    
        /*手动加载水印*/
        watermark.load = function (settings) {
            loadMark(settings);
            observerDomReloadMark(settings)
        };
    
        return watermark;
    }))

    2、优化

    但是毕竟水印是有关安全方面的,如果别人打开开发者模式很轻松的就水印的内容更改甚至完全删除,所以不能就这么完事了。我们需要监听DOM的变化,判断如果用户通过开发者工具手动修改了水印的dom,我们就重新渲染水印,实现水印不能手动进行了修改。

    思路:通过mutationObsever监听DOM的变化,判断是否是水印部分的dom 变化,如果是则重新渲染

    show me the code:

    /* 监听DOM的变化,防止手动删除 */
        let observerDomReloadMark = (settings) => {
            // Select the node that will be observed for mutations
            let observer_node = document.querySelector('#shadowContainer')
            // Options for the observer (which mutations to observe)
            let config = {
                attributes: true,
                childList: true,
                subtree: true
            };
            // Callback function to execute when mutations are observed
            const mutationCallback = (mutationsList) => {
                // for (let mutation of mutationsList) {
                //     let type = mutation.type;
                //     switch (type) {
                //         case "childList":
                //             console.log("A child node has been added or removed.");
                //             break;
                //         case "attributes":
                //             console.log(`The ${mutation.attributeName} attribute was modified.`);
                //             break;
                //         case "subtree":
                //             console.log(`The subtree was modified.`);
                //             break;
                //         default:
                //             break;
                //     }
                // }
                // loadMark(settings)
                console.log(2222)
            };
    
            // Create an observer instance linked to the callback function
            let observer = new MutationObserver(mutationCallback);
    
            // Start observing the target node for configured mutations
            observer.observe(observer_node, config);
    
            // Later, you can stop observing
            // observer.disconnect();
        }

    这个方法看起来完美,然而现实很残酷,当我们再浏览器中看效果的时候发现mutationObsever不能监听shadow的变化,what? 查资料后发现shadow是独立于dom树的,相当于被隔离起来的dom。既然如此那就放弃装逼吧,还是老老实实用正常的dom实现吧,有多少个水印就生成多个水印的dom,这必要会导致性能问题。好吧,我们还是再另寻他法。

    二、canvas绘图

    1、基本思路

    通过canvas把单个水印绘制出来,用toDataURL方法生成图片的base64编码,放在水印容器的background-image:url()里面,背景图片会自动铺满整个容器。思想比第一种方案简单很多。

    show me the code:

          var canvas = document.createElement('canvas')
          canvas.id = 'canvas'
          canvas.width = defaultSettings.watermark_width // 单个水印的宽度
          canvas.height = defaultSettings.watermark_height // 单个水印的高度
          var ctx = canvas.getContext('2d')
          ctx.font = 'normal 12px Microsoft Yahei' // 设置样式
          ctx.fillStyle = 'rgba(112, 113, 114, 0.2)' // 水印字体颜色
          ctx.translate(canvas.width/2,canvas.height/2)
          ctx.rotate((defaultSettings.watermark_angle * Math.PI) / 180) // 水印偏转角度
          ctx.translate(-canvas.width/2,-canvas.height/2)
          ctx.fillText(defaultSettings.watermark_txt, 30, 20)
          var src = canvas.toDataURL('image/png')
    
          /* 设置水印的容器 */
          var watermark_parent_node = null
          if (defaultSettings.watermark_parent_selector) {
              watermark_parent_node = document.querySelector(defaultSettings.watermark_parent_selector)
          } else {
              watermark_parent_node = document.body
          }
         watermark_parent_node.style.pointerEvents = 'none'
          // 判断容器是否已经设置了backgroundImage属性值
          if (!watermark_parent_node.style.backgroundImage) {
              watermark_parent_node.style.backgroundImage = 'URL(' + src + ')'
          } else {
              watermark_parent_node.style.backgroundImage = watermark_parent_node.style.backgroundImage + ', URL(' + src + ')'
          }

    PPT模板下载大全https://redbox.wode007.com

    2、优化

    优化思路和第一种方案一样,只不过这种方案的好处就是可以直接监听到变化。

    三、两种方案的对比

    方案 优点 缺点
    shadow DOM  1、低耦合,shadow dom与原先的DOM树隔离,不会影响到系统本有的功能。

    1、shadow DOM不能够被监听到

    2、水印文案及DOM被篡改的成本较低

    3、实现逻辑比较复杂

    canvas绘图

    1、实现逻辑比较清晰

    2、水印数据生成图片,用户想篡改比较难

    3、水印被篡改能够被监听到

    1、水印图片是被放在background-image里面的,如果原先在class里面设置了background-image属性的话会被覆盖掉

  • 相关阅读:
    Android开发之 Android 的基本组件的概述
    Android开发之 Android应用程序详细解析
    Android开发之 Android应用程序目录结构解析
    第七篇 :微信公众平台开发实战Java版之如何获取微信用户基本信息
    Android 开发之Windows环境下Android Studio安装和使用教程(图文详细步骤)
    Android开发之 Windows环境下通过Eclipse创建的第一个安卓应用程序(图文详细步骤)
    Android 开发之 Android 开发的起步
    Github学习进阶-初露锋芒,通过命令行将本地git仓库推送到Github上面的仓库
    Windows环境下maven 安装与环境变量配置
    Github学习之路-小试牛刀,练习Git 的基本操作
  • 原文地址:https://www.cnblogs.com/xiaonian8/p/14041603.html
Copyright © 2020-2023  润新知