深入探讨前端水印
1、如何给页面添加定制水印
定制水印一般指的是可以指定任何文字,给页面添加定制水印有多种方法:
- 背景图方式
- 创建元素,悬浮在页面之上的方式
1.1、通过背景图给页面添加定制水印(会被子元素的背景覆盖)
通过背景图添加定制水印:
- 首先制做定制水印,定制水印可以通过 canvas 绘画出来
- 然后通过 background-image 属性将 canvas 制做的水印作为元素的背景图添加进去
<div id="wrapDiv" style="background-color: antiquewhite"> <h1>添加水印。。。。。</h1> <button onclick="btnClick()">点击事件</button> <input type="text"> <br> <button onclick="btnClick()">点击事件</button> <input type="text"> <br> </div> <script> function addWaterMarker(divId, str) { //创建一个画布 var can = document.createElement('canvas'); var waterMarkerDiv = document.getElementById(divId); waterMarkerDiv.appendChild(can); //设置画布的样式 can.width = 400; can.height = 300; can.style.display = 'none'; //创建context对象 var ctx = can.getContext('2d'); //将context旋转-20度 ctx.rotate(-20 * Math.PI / 180); //定义字体样式 ctx.font = "20px Microsoft JhengHei"; //设置字体颜色 ctx.fillStyle = "rgba(17, 17, 17, 0.50)"; //文本横向对齐方式 ctx.textAlign = 'left'; //文本纵向对齐方式 ctx.textBaseline = 'Middle'; //绘制实心文本,初始横坐标为画布宽度的1/3,初始纵坐标为画布高度的一半 ctx.fillText(str, can.width / 3, can.height / 2); //将画布设为元素的背景,并且重复,达成水印的效果 waterMarkerDiv.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")"; } addWaterMarker('wrapDiv', '定制的水印123。。。') </script>
效果:
可以看到用背景图添加水印有一个很大的缺点,那就是当添加了背景水印的元素的子元素有背景颜色或者是背景图时,将会把该元素的水印给遮挡住,当子元素很多,并且都需要有背景时,这时候水印可能会导致被几乎全部遮挡住,并且往往水印的要求是需要悬浮在页面所有元素之上的。
1.2、创建元素,悬浮在页面之上的方式(只适用于 IE11及以上版本)
动态创建元素的方式:
- 动态创建元素,往元素里面添加需要定制的文本内容
- 设置元素悬浮在页面之上,通过absolute、z-index属性
- 设置创建的元素 pointer-events 为 none,则悬浮在页面之上的元素将不会捕获到任何事件,事件将会直接触发悬浮元素之下的元素
这种方式的关键在于 pointer-events 属性,将该属性设置为 none,则该元素将不会是鼠标事件的目标,鼠标事件会“穿透”该元素。
//动态创建水印元素的封装函数 shuiyin.js function watermark(settings) { //默认设置 var defaultSettings = { watermark_txt: "text", watermark_x: 20, //水印起始位置x轴坐标 watermark_y: 20, //水印起始位置Y轴坐标 watermark_rows: 20, //水印行数 watermark_cols: 20, //水印列数 watermark_x_space: 100, //水印x轴间隔 watermark_y_space: 50, //水印y轴间隔 watermark_color: '#aaa', //水印字体颜色 watermark_alpha: 0.4, //水印透明度 watermark_fontsize: '15px', //水印字体大小 watermark_font: '微软雅黑', //水印字体 watermark_width: 210, //水印宽度 watermark_height: 80, //水印长度 watermark_angle: 20 //水印倾斜度数 }; //根据函数的入参调整设置 if (arguments.length === 1 && typeof arguments[0] === "object") { var src = arguments[0] || {}; for (key in src) { if (src[key] && defaultSettings[key] && src[key] === defaultSettings[key]) continue; else if (src[key]) defaultSettings[key] = src[key]; } } //创建虚拟节点对象,在该节点对象中可以放元素,最后只需在页面中添加该节点对象即可。可提高性能 var oTemp = document.createDocumentFragment(); //获取页面最大宽度 var page_width = Math.max(document.body.scrollWidth, document.body.clientWidth); var cutWidth = page_width * 0.0150; var page_width = page_width - cutWidth; //获取页面最大高度 var page_height = Math.max(document.body.scrollHeight, document.body.clientHeight) + 450; page_height = Math.max(page_height, window.innerHeight - 30); //如果将水印列数设置为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 + defaultSettings.watermark_x_space) / (defaultSettings.watermark_width + defaultSettings.watermark_x_space)); defaultSettings.watermark_x_space = parseInt((page_width - defaultSettings.watermark_x - 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((defaultSettings.watermark_y_space + page_height - defaultSettings.watermark_y) / (defaultSettings.watermark_height + defaultSettings.watermark_y_space)); defaultSettings.watermark_y_space = parseInt(((page_height - defaultSettings.watermark_y) - defaultSettings.watermark_height * defaultSettings.watermark_rows) / (defaultSettings.watermark_rows - 1)); } var x; var y; console.log('水印行数', defaultSettings.watermark_rows); console.log('水印列数', defaultSettings.watermark_cols); for (var i = 0; i < defaultSettings.watermark_rows; i++) { y = defaultSettings.watermark_y + (defaultSettings.watermark_y_space + defaultSettings.watermark_height) * i; for (var j = 0; j < defaultSettings.watermark_cols; j++) { x = defaultSettings.watermark_x + (defaultSettings.watermark_width + defaultSettings.watermark_x_space) * j; var mask_div = document.createElement('div'); mask_div.id = 'mask_div' + i + j; mask_div.className = 'mask_div'; mask_div.appendChild(document.createTextNode(defaultSettings.watermark_txt)); //设置水印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 = "9999"; //让水印不遮挡页面的点击事件 mask_div.style.pointerEvents = 'none'; 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"; oTemp.appendChild(mask_div); }; }; document.body.appendChild(oTemp); } function getNow() { var d = new Date(); var year = d.getFullYear(); var month = change(d.getMonth() + 1); var day = change(d.getDate()); var hour = change(d.getHours()); var minute = change(d.getMinutes()); var second = change(d.getSeconds()); function change(t) { if (t < 10) { return "0" + t; } else { return t; } } var time = year + '年' + month + '月' + day + '日 ' + hour + '时' + minute + '分' + second + '秒'; return time; }
<div> <h6>测试页面 测试页面</h6> <button onclick="showInfo()">点击</button> <input type="text"> <button onclick="showInfo()">点击</button> <input type="text"> <button onclick="showInfo()">点击</button> <input type="text"> <button onclick="showInfo()">点击</button> <input type="text"> </div> <script src="shuiyin.js"></script> <script type="text/javascript"> function showInfo() { console.log(111); } var now = getNow(); watermark({ "watermark_txt": "A B C X Y Z " + now }); </script>
效果:
可以看到,子元素的背景图并不会把水印给挡住。
这种方式创建水印在 Chrome 及 IE11+ 版本的浏览器上是没有问题的,但如果在IE11以下的版本,那就会出现问题:虽然展示的效果没有差别,但是 pointer-events 属性不再起作用,水印元素会捕获到事件,并且水印元素能被选中,导致在水印之下的元素无法被事件捕获到。
这是因为 pointer-events 属性只能兼容到 IE11 版本的浏览器:
2、给页面添加水印如何兼容IE11以下浏览器
给页面添加水印兼容IE11以下浏览器,可以使用 SVG + 背景图 的方式。在 SVG 元素上使用 pointer-events 属性可以兼容低版本 IE,经测试可以兼容至 IE9,然后我们就可以给 SVG 元素添加水印作为背景图,将 svg 元素悬浮在页面之上,就可以实现添加水印的效果。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> #wrapDiv { height: 500px; width: 100%; background-color: antiquewhite; } #svgDom { /* background-color: gold; */ opacity: .5; height: 100%; width: 100%; position: absolute; top: 0; pointer-events: none; } </style> </head> <body> <div id="wrapDiv"> <h1>添加水印。。。。。</h1> <button onclick="btnClick()">点击事件</button> <input type="text"> <br> <button onclick="btnClick()">点击事件</button> <input type="text"> <br> <button onclick="btnClick()">点击事件</button> <input type="text"> <br> <button onclick="btnClick()">点击事件</button> <input type="text"> <br> <button onclick="btnClick()">点击事件</button> <input type="text"> <br> <button onclick="btnClick()">点击事件</button> <input type="text"> <br> <button onclick="btnClick()">点击事件</button> <input type="text"> <br> <button onclick="btnClick()">点击事件</button> <input type="text"> <br> </div> <svg id="svgDom"></svg> <script> function btnClick() { console.log(123123); } function addWaterMarker(divId, str) { //创建一个画布 var can = document.createElement('canvas'); var waterMarkerDiv = document.getElementById(divId); waterMarkerDiv.appendChild(can); //设置画布的样式 can.width = 400; can.height = 200; can.style.display = 'none'; //创建context对象 var ctx = can.getContext('2d'); //将context旋转-20度 ctx.rotate(-20 * Math.PI / 180); //定义字体样式 ctx.font = "20px Microsoft JhengHei"; //设置字体颜色 ctx.fillStyle = "rgba(17, 17, 17, 0.50)"; //文本横向对齐方式 ctx.textAlign = 'left'; //文本纵向对齐方式 ctx.textBaseline = 'Middle'; //绘制实心文本,初始横坐标为画布宽度的1/3,初始纵坐标为画布高度的一半 ctx.fillText(str, can.width / 3, can.height / 2); //将画布设为元素的背景,并且重复,达成水印的效果 waterMarkerDiv.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")"; } addWaterMarker('svgDom', 'SVG定制水印123。。。') </script> </body> </html>
效果:
此时在低版本 IE 下水印不会捕获到事件,水印也不会被选中,可以实现在低版本 IE(9)下实现水印的效果。
另外,也可以通过获取在点击的水印的位置下的元素,然后再主动触发该元素的点击事件来达到水印不捕获事件的效果。(但是这种方式有点麻烦,因为你不知道水印下的元素被绑定的是点击事件还是聚焦事件,或者是其他事件,这样你就不知道应该触发什么事件)
可参考:https://doc.weixiaoduo.com/knowledgebase/23002.html
3、暗水印