目录
canvas自适应文字长度,旋转角度生成水印背景图
生成蒙层
禁止蒙层的删除和修改

ctx.measureText(text).width获取两行文字的宽度text1,text2,取最大宽度为文本框宽度textWidthtextHeight=2*fontsize+ space_linerad = (rotate*Math.pi) /180 function drawWatermark(el, config = {}) {if (!el) return;// 默认配置let {text1 = '今天也要保持愉悦鸭~', //文本1text2 = '2022-12-07', // 文本2space_x = 0, // x轴间距 space_y = 0, // y轴间距space_line = 20, //两列文字的间距font = 'Microsoft JhengHei bold',fontSize = 40, // 字体color = 'rgba(22,22,22,1)', // 字色rotate = 30 // 倾斜度} = config;const canvas = document.createElement('canvas');el.appendChild(canvas);const ctx = canvas .getContext('2d');ctx.font = fontSize + 'px ' + font; //设置好fontsize才能正确计算出文本宽度let tw1= ctx.measureText(text1).width;let tw2= ctx.measureText(text2).width;let textWidth = Math.max(tw1, tw2); //文本最长宽度为文本框宽度let textHeight = fontSize * 2 + space_line; //文本框高度为两个文本+行间距let rad = (rotate * Math.PI) / 180; //角度转弧度let sin = Math.sin(rad ); let cos = Math.cos(rad );let width = textWidth * cos + textHeight * sin + space_x * 2; (textWidth * cos + textHeight * sin)//为包裹住文本框的最小盒子宽度let height = textWidth * sin + textHeight * cos + space_y * 2;(textWidth * sin + textHeight * cos)//为包裹住文本框的最小盒子高度canvas.width = width;canvas.height = height;canvas.style.cssText = `width:${width}px;height:${height}px;display:none;`;ctx.translate(space_x , textWidth * sin + space_y ); // 移动旋转中心ctx.rotate((-1 * (rotate * Math.PI)) / 180); //旋转文本框ctx.fillStyle = color;ctx.textAlign = 'left';ctx.textBaseline = 'middle';ctx.font = fontSize + 'px ' + font;ctx.fillText(text1, (textWidth - tw1) / 2, 0.5 * fontSize); //文本在文本框中居中显示ctx.fillText(text2, (textWidth - tw2) / 2, 1.5 * fontSize + space_line); //文本在文本框中居中显示return canvas.toDataURL('image/png');},
在目标元素下添加一个相对定位的子元素,将水印图片平铺作为背景图。
function createMask(el) {//创建蒙层let $mask = document.createElement('div');//判断蒙层父元素是否有定位let position = window.getComputedStyle(el, null).position;if (position === 'static') {el.style.position = 'relative';}//设置蒙层样式let style = `visibility: visible !important;transform: translate(0,0) !important;display: block !important;visibility: visible !important;width: 100% !important; height: 100% !important;background-color: rgba(0, 0, 0, 0)!important;background-repeat: repeat !important;position: absolute !important;top: 0px !important;left: 0px !important;z-index: 999 !important; background-image: url(${drawWatermark(el)}) !important`;$mask.setAttribute('style', style);//添加蒙层el.append($mask);// 创建MutationObserverel.observer = new MutationObserver((mutationRecord) => {//处理DOMmutationRecord.forEach((mutation) => {// 蒙层删除或者被移动到别处if (mutation.target === el && mutation.removedNodes[0] == $mask) {el.append($mask);} else if (mutation.target == $mask && mutation.attributeName === 'style') {// 蒙层被更改样式 在监听到蒙层样式更改后,赋值的新的样式会导致再次触发监听回调,所以需要在监听事件中判断何时需要赋值const changestyle = $mask?.getAttribute('style');if (changestyle !== style) {$mask.setAttribute('style', style);}}});});// 启动监控el.observer.observe(el, {childList: true,attributes: true,subtree: true});return $mask;},
行内样式加important是为了防止通过添加class或其他css覆盖样式(暂时没有找到怎么如何通过修改css的方式更改样式的监听方式)
踩得一个坑
设置元素的行内样式有很多种
方式二设置样式后,行内样式格式和赋值时的style的格式不一样,获取到行内style后直接进行===判断,回造成死循环
解决方法: