自适应且不可删除的水印蒙层
创始人
2024-03-28 05:25:16

目录

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

生成蒙层

禁止蒙层的删除和修改


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 = '今天也要保持愉悦鸭~', //文本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');},

生成蒙层

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

禁止蒙层的删除和修改

  • 删除或移动element
  • 修改style
 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后直接进行===判断,回造成死循环

解决方法:

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

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...