We propose two solutions for watermarking project
1. Implementation with shadow dom
1. Basic Ideas
By attach Shadow, a shadow root node is generated, and then a watermarking is added under the root node through a circular statement. The position is used to layout the absolute and make it full of containers.
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 = {}; /*Loading watermarking*/ let loadMark = function () { let defaultSettings = { watermark_id: 'wm_div_id', //id of total watermarking watermark_prefix: 'mask_div_id', //id prefix of small watermarking watermark_txt: "Test watermarking", //Watermarking Content watermark_x: 20, //x-axis coordinates of watermarking starting position watermark_y: 40, //Y-axis coordinates of watermarking starting position watermark_rows: 0, //Number of watermarking rows watermark_cols: 0, //Watermark column number watermark_x_space: 100, //Watermarking x-axis interval watermark_y_space: 50, //Watermarking y-axis interval watermark_font: 'Microsoft YaHei', //Watermark font watermark_color: 'black', //Watermark font color watermark_fontsize: '18px', //Watermark font size watermark_alpha: 0.15, //Watermark transparency, set to greater than or equal to 0.005 watermark_width: 100, //Watermark Width watermark_height: 100, //Watermark Length watermark_angle: 15, //Watermark Tilt Degree watermark_parent_selector: null, //The parent element selector mounted by the watermarking plug-in is hung on the body by default without input. need_adapt_screen: false, // Whether to change the width and font size of each watermarking according to the resolution ratio of the screen watermark_width_proportion: 15, // Equivalent scaling-up or scaling-down values for each watermarking width adapting to screen changes watermark_fontsize_proportion: 95, // Equivalent scaling-up or scaling-down values for each watermarking font size adapting to screen changes }; let watermark_parent_node = null //The parent element mounted by the watermarking plug-in is hung on the body by default without input. let setting = arguments[0] || {}; /*Replace defaults with configuration items, similar to 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]; } } /* Watermarking Containers */ if (defaultSettings.watermark_parent_selector) { watermark_parent_node = document.querySelector(defaultSettings.watermark_parent_selector) } else { watermark_parent_node = document.body } /*If an element exists, remove it*/ let watermark_element = document.getElementById(defaultSettings.watermark_id); if (watermark_element) { let _parentElement = watermark_element.parentNode; if (_parentElement) { _parentElement.removeChild(watermark_element); } } /*Getting the Initial Position of Watermark*/ 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; /*Create a watermarking shell div*/ let otdiv = document.getElementById(defaultSettings.watermark_id); let shadowRoot = null; if (!otdiv) { otdiv = document.createElement('div'); /*Create shadow dom*/ otdiv.id = defaultSettings.watermark_id; otdiv.style.pointerEvents = "none"; /*Determining whether browsers support attachShadow methods*/ 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 with a container let shadowOutDiv = document.createElement('div') shadowOutDiv.id = 'shadowContainer' shadowRoot.appendChild(shadowOutDiv) /*If the number of watermarking columns is set to 0, or if the number of watermarking columns is too large to exceed the maximum width of the page, the number of watermarking columns and the interval between the x-axis of the watermarking are recalculated.*/ 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)); } /*If the number of watermarking rows is set to 0, or if the number of watermarking rows is too large to exceed the maximum length of the page, the number of watermarking rows and the y-axis interval of the watermarking are recalculated.*/ 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); /*Setting Watermark Related Attribute start*/ mask_div.id = defaultSettings.watermark_prefix + i + j; /*Setting Watermark Divid Tilt Display*/ 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"; /*Unchosen*/ 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"; /*Setting Watermark Related Attribute end*/ shadowOutDiv.appendChild(mask_div); } } }; /*Initialize watermarking, add load and resize events*/ watermark.init = function (settings) { window.addEventListener('load', function () { loadMark(settings); }); window.addEventListener('resize', function () { loadMark(settings); }); window.addEventListener('DOMContentLoaded', function () { loadMark(settings); }); }; /*Manual loading of watermarking*/ watermark.load = function (settings) { loadMark(settings); observerDomReloadMark(settings) }; return watermark; }))
2. Optimizing
But after all, watermarking is related to security. If someone opens the developer's mode easily, the content of the watermarking can be changed or even deleted completely, so it can't be done. We need to monitor the changes of DOM and judge that if the user manually modifies the dom of the watermarking through the developer's tool, we will re-render the watermarking, so that the watermarking can not be modified manually.
Idea: By mutation Obsever to monitor the changes of DOM, to determine whether it is the changes of the watermarking part of the dom, and if so, to re-render?
show me the code:
/* Monitor DOM changes to prevent manual deletion */ 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(); }
This method seems perfect, but the reality is very cruel. When we look at the effect in the browser again, we find mutation Obsever can't monitor the change of shadow. what? After looking up the data, we find that shadow is independent of the DOM tree, which is equivalent to the isolated dom. In this case, let's give up pretension, or honest and practical normal DOM implementation, how many watermarks will generate multiple watermarks dom, which will necessarily lead to performance problems. Well, let's find another way.
2. canvas Drawing
1. Basic Ideas
A single watermarking is drawn by canvas, and the base64 encoding of the image is generated by toDataURL method, which is placed in background-image: url() of the watermarking container, and the background image will automatically fill the whole container. The idea is much simpler than the first one.
show me the code:
var canvas = document.createElement('canvas') canvas.id = 'canvas' canvas.width = defaultSettings.watermark_width // Width of a single watermarking canvas.height = defaultSettings.watermark_height // Height of a single watermarking var ctx = canvas.getContext('2d') ctx.font = 'normal 12px Microsoft Yahei' // Setting Styles ctx.fillStyle = 'rgba(112, 113, 114, 0.2)' // Watermark font color ctx.translate(canvas.width/2,canvas.height/2) ctx.rotate((defaultSettings.watermark_angle * Math.PI) / 180) // Watermarking deflection angle ctx.translate(-canvas.width/2,-canvas.height/2) ctx.fillText(defaultSettings.watermark_txt, 30, 20) var src = canvas.toDataURL('image/png') /* Watermarking Containers */ 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' // Determine whether the container has set the backgroundImage property value 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 + ')' }
2. Optimizing
The idea of optimization is the same as that of the first scheme, but the advantage of this scheme is that it can directly monitor changes.
III. Contrast of the two schemes
programme | Advantage | shortcoming |
shadow DOM | 1. Low coupling and isolation of shadow dom from the original DOM tree will not affect the intrinsic functions of the system. | 1. shadow DOM cannot be monitored 2. The cost of tampering with watermarking copy and DOM is low. 3. Complex logic of implementation |
canvas drawing | 1. Clear implementation logic 2. It is difficult for users to tamper with watermarking data to generate images. 3. Watermarks can be monitored if tampered with
|
1. The watermarking image is placed in background-image. If the background-image attribute is set in the class, it will be overwritten. |