preface
Page watermarking is a means of digital protection, copyright protection and intellectual property protection. In order to prevent others from stealing, it can quickly locate individuals
target
- To improve security, the hidden watermark cannot be modified through the console
- The front-end watermark does not affect the normal use of the page
- The display mode of the front watermark is that the whole page rotates at a certain angle
technical analysis
- If the front-end web page wants to display the watermark as a whole, first generate a watermark
- Then fill the whole page with the watermark as the background image (repeat the watermark)
- Then detect the watermark change, and if the change is regenerated. Will not be hidden.
Implementation scheme
First, a watermark is generated
- Can be generated through canvas
function canvasWM({ container = document.body, width = '200px', height = '200px', textAlign = 'center', textBaseLine = 'middle', font = '12px SourceHanSansSCVF-Regular', fillStyle = 'rgba(0,0,0,0.1)', content = 'Front end watermark', zIndex = 10 } = {}) { var args = arguments[0]; var canvas = document.createElement('canvas'); canvas.setAttribute('width', width); canvas.setAttribute('height', height); var ctx = canvas.getContext('2d'); ctx.textAlign = textAlign; ctx.textBaseline = textBaseline; ctx.font = font; ctx.fillStyle = fillStyle; ctx.fillText(content, parseFloat(width) / 2, parseFloat(height) / 2); // var base64Url = canvas.toDataURL(); } // Calling canvasWM will generate a base64Url watermark image
Link: How canvas generates dataUrl
The picture generated by the above code
- Can be generated by svg (similar to canvas)
function svgWM({ container = document.body, width = '200px', height = '200px', fontSize = '12px', fontFamily = 'SourceHanSansSCVF-Regular', fill= 'rgb(0,0,0,0.06)', content = 'Front end watermark', zIndex = 10 } = {}) { const args = arguments[0]; const svgStr = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"> <text x="50%" y="50%" text-anchor="middle" font-family=${fontFamily} fill=${fill} style="font-size: ${fontSize};"> ${content} </text> </svg>`; const base64Url = `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svgStr)))}`; // Calling svgWM will generate a base64Url watermark image }
Using watermark as background image
The above code implements a watermark image, and then creates a div element to take the watermark as the background image
const __wm = document.querySelector('.__wm'); const watermarkDiv = __wm || document.createElement("div"); const styleStr = ` position:fixed; top:0px; left:0px; width:100%; height:1400%; z-index:${zIndex}; pointer-events:none; // The element will never be the target of a mouse event background-repeat:repeat; // Repeat watermark display transform: rotate(-24deg) translateZ(0); // Overall page rotation angle overflow:hidden; background-image:url('${base64Url}')`; watermarkDiv.setAttribute('style', styleStr); watermarkDiv.classList.add('__wm');
The rotation in the above code will cause the watermark font on the image to be blurred, which has not been solved for the time being
Detect watermark changes and make adjustments
The MutationObserver interface provides the ability to monitor changes to the DOM tree
The rotation observer API is used to monitor DOM changes. The API can be notified of any changes to the DOM, such as the increase or decrease of nodes, the change of attributes, and the change of text content.
Use the MutationObserver constructor to create a new observer instance. The instance has a callback function that accepts two parameters, the first is the variable array and the second is the observer instance. The observe method of the instance of MutationObserver is used to start listening. It accepts two parameters
The MutationObserver can only detect changes in attributes, addition and deletion of child nodes, etc. there is no way to delete itself. It can meet the requirements by monitoring the parent node
if (!__wm) { container.style.position = 'relative'; container.insertBefore(watermarkDiv, container.firstChild); } const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; if (MutationObserver) { let mo = new MutationObserver(function () { const __wm = document.querySelector('.__wm'); // Only in__ The wm element is called again only after it changes__ canvasWM if ((__wm && __wm.getAttribute('style') !== styleStr) || !__wm) { // Avoid constant triggering mo.disconnect(); mo = null; __canvasWM(JSON.parse(JSON.stringify(args))); } }); mo.observe(container, { attributes: true, subtree: true, childList: true }) }
call
// Call after import canvasWM({ content: 'QQMusicFE' });
Overall code
(function () { function svgWM({ container = document.body, width = '200px', height = '200px', fontSize = '12px', fontFamily = 'SourceHanSansSCVF-Regular', fill= 'rgb(0,0,0,0.06)', content = 'Front end watermark', zIndex = 10 } = {}) { const args = arguments[0]; const svgStr = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"> <text x="50%" y="50%" text-anchor="middle" font-family=${fontFamily} fill=${fill} style="font-size: ${fontSize};"> ${content} </text> </svg>`; const base64Url = `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svgStr)))}`; const __wm = document.querySelector('.__wm'); const watermarkDiv = __wm || document.createElement("div"); const styleStr = ` position:fixed; top:0px; left:0px; width:100%; height:1400%; z-index:${zIndex}; pointer-events:none; // The element will never be the target of a mouse event background-repeat:repeat; // Repeat watermark display transform: rotate(-24deg) translateZ(0); // Overall page rotation angle overflow:hidden; background-image:url('${base64Url}')`; watermarkDiv.setAttribute('style', styleStr); watermarkDiv.classList.add('__wm'); if (!__wm) { container.style.position = 'relative'; container.insertBefore(watermarkDiv, container.firstChild); } const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; if (MutationObserver) { let mo = new MutationObserver(function () { const __wm = document.querySelector('.__wm'); // Only in__ The wm element is called again only after it changes__ canvasWM if ((__wm && __wm.getAttribute('style') !== styleStr) || !__wm) { // Avoid constant triggering mo.disconnect(); mo = null; __canvasWM(JSON.parse(JSON.stringify(args))); } }); mo.observe(container, { attributes: true, subtree: true, childList: true }) } } if (typeof module != 'undefined' && module.exports) { //CMD module.exports = __canvasWM; } else if (typeof define == 'function' && define.amd) { // AMD define(function () { return __canvasWM; }); } else { window.__canvasWM = __canvasWM; } })();