ctx.measureText(text).width
獲取兩行文字的寬度text1,text2,取最大寬度為文字方塊寬度textWidthtextHeight=2*fontsize+ space_line
rad = (rotate*Math.pi) /180
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');
},
在目標元素下新增一個相對定位的子元素,將水印圖片平鋪作為背景圖。
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後直接進行===判斷,回造成死迴圈
解決方法: