自适应且不可删除的水印蒙层

  • 自适应且不可删除的水印蒙层已关闭评论
  • 37 次浏览
  • A+
所属分类:Web前端
摘要

在目标元素下添加一个相对定位的子元素,将水印图片平铺作为背景图。transform: translate(100%,100%);
display: none;
visibility: hidden;
取消背景图

自适应且不可删除的水印蒙层

canvas自适应文字长度,旋转角度生成水印背景图

自适应且不可删除的水印蒙层

  • 设置canvas字体大小后,通过ctx.measureText(text).width获取两行文字的宽度text1,text2,取最大宽度为文本框宽度textWidth
  • 设置两行文字间距,可得文本框高度:textHeight=2*fontsize+ space_line
  • 计算最小一个能够完全包裹旋转后文本的盒子宽高
    已知旋转角度为rotate=>得到弧度rad = (rotate*Math.pi) /180
    单个水印图平铺成为蒙层的背景图,space_x,space_y用于调整水印之间的间距
    自适应且不可删除的水印蒙层
  function  drawWatermark(el, config = {}) {       if (!el) return;       // 默认配置       let {         text1 = '今天也要保持愉悦鸭~', //文本1         text2 = '2022-12-07', // 文本2         space_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 ; //为包裹住文本框的最小盒子宽度       let height = textWidth * sin + textHeight * cos + space_y; //为包裹住文本框的最小盒子高度       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');     }, 

生成蒙层

在目标元素下添加一个相对定位的子元素,将水印图片平铺作为背景图。

禁止蒙层的删除和修改

  • 删除或移动element
  • 修改style

transform: translate(100%,100%);
display: none;
visibility: hidden;
取消背景图

 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;         pointer-events: none !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);       // 创建MutationObserver       el.observer = new MutationObserver((mutationRecord) => {         //处理DOM         mutationRecord.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的方式更改样式的监听方式)
踩得一个坑
设置元素的行内样式有很多种

let style = 'width:100%;height:100%' 适用单个样式更改
element.style.width =100% ;element.style['height'] =100%
element.style.cssText = style
element.setAttribute('style',style )

方式二设置样式后,行内样式格式和赋值时的style的格式不一样,获取到行内style后直接进行===判断,回造成死循环

解决方法:

  • 第一次监听到蒙层更改时,立刻移除蒙层,重新生成新蒙层
  • 写个函数判断不同格式的两个样式属性上是否相等