關(guān)于JS前端實(shí)現(xiàn)水印的代碼操作
網(wǎng)頁水印
實(shí)現(xiàn)思路
- 通過canvas生成一張水印圖片
- 通過css將圖片設(shè)置為目標(biāo)節(jié)點(diǎn)的背景圖
- 通過MutationObserver監(jiān)聽目標(biāo)節(jié)點(diǎn)的類名變化,防止水印被刪除
代碼操作
- 通過canvas生成一張水印圖片
function createImgBase(options) {
const { content, width, height } = options;
const canvasDom = document.createElement("canvas");
let ctx = canvasDom.getContext("2d");
canvasDom.width = width;
canvasDom.height = height;
if (ctx) {
// 設(shè)置畫筆的方向
ctx.rotate((-14 * Math.PI) / 180);
// 設(shè)置水印樣式
ctx.fillStyle = "rgba(100,100,100,0.4)";
ctx.font = "italic 20px Arial";
// 渲染水印
content.forEach((text, index) => {
ctx.fillText(text, 10, 30 * (index + 1)); // 縱向拉開30的間距
});
}
// document.body.appendChild(canvasDom);
// 將canvas轉(zhuǎn)為圖片
return canvasDom.toDataURL("image/png");
}
// createImgBase({
// content: ["介四嘛呀", "介四sui印", "內(nèi)部機(jī)密材料", "嚴(yán)禁外泄!"],
// width: 200,
// height: 200,
// });
- 將水印設(shè)置為目標(biāo)節(jié)點(diǎn)的背景圖片
function getWaterMark({
content,
className,
canvasHeight = 140,
canvasWidth = 150,
}) {
// 生成圖片
const data_url = createImgBase({
content,
width: canvasWidth,
height: canvasHeight,
});
// 通過設(shè)置偽元素樣式,添加水印圖片為背景圖
const defaultStyle = `
.${className} {
position: relative;
}
.${className}::after {
content: "";
background-image: url(${data_url});
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
}`;
const styleDom = document.createElement("style");
styleDom.innerHTML = defaultStyle;
document.head.appendChild(styleDom);
}
// getWaterMark({
// content: ["介四嘛呀", "介四sui印", "內(nèi)部機(jī)密材料", "嚴(yán)禁外泄!"],
// className: "content",
// });
- 添加mutationObserver監(jiān)聽節(jié)點(diǎn)的變化
function listenerDOMChange(className) {
// 獲取要監(jiān)聽的節(jié)點(diǎn)
const targetNode = document.querySelector(`.${className}`);
// 創(chuàng)建監(jiān)聽器
const observer = new MutationObserver(mutationList => {
// 遍歷變化記錄
for (let mutationRecord of mutationList) {
// 如果目標(biāo)節(jié)點(diǎn)的class屬性發(fā)生變化,判斷是不是類名被刪了,是的話把類名加回去
if (mutationRecord.attributeName === "class") {
if(!Array.from(targetNode.classList).includes(className)) {
targetNode.classList.add(className)
}
}
}
});
// 啟動監(jiān)聽
observer.observe(targetNode, {
attributes: true,
});
}
function getWaterMark({
content,
className,
canvasHeight = 140,
canvasWidth = 150,
}) {
// 監(jiān)聽
listenerDOMChange(className);
const data_url = createImgBase({
content,
width: canvasWidth,
height: canvasHeight,
});
// ...
const styleDom = document.createElement("style");
styleDom.innerHTML = defaultStyle;
document.head.appendChild(styleDom);
}
關(guān)于MutationObserver
MutationObserver 用來監(jiān)聽DOM的變化,DOM的增刪、DOM屬性的變化,子結(jié)點(diǎn)和文本內(nèi)容的變化,都可以被監(jiān)聽。
MutationObserver 的監(jiān)聽和事件不同,事件是同步的,DOM的變化會立即觸發(fā)對應(yīng)的事件,而 MutationObserver 是異步的,會在下一個微任務(wù)執(zhí)行時觸發(fā)監(jiān)聽回調(diào)。
- 創(chuàng)建MutationObserver
const observer = new MutationObserver((mutationsList, observer) => {
// mutationsList mutationRecord數(shù)組 記錄了DOM的變化
// observer MutationObserver的實(shí)例
// 監(jiān)聽回調(diào)
console.log(mutationsList, observer);
})
mutationObserver.observe(document.documentElement, {
attributes: true,
characterData: true,
childList: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true
});
- 開啟監(jiān)聽
// node 監(jiān)聽的節(jié)點(diǎn)
// config 監(jiān)聽配置(要監(jiān)聽哪些內(nèi)容)
// observer.observe(node, config);
mutationObserver.observe(document.documentElement, {
attributes: true, // 屬性變化
attributeOldValue: true, // 觀察attributes變動時,是否需要記錄變動前的屬性值
attributeFilter: [‘class',‘src'] // 需要觀察的特定屬性
characterData: true, // 節(jié)點(diǎn)內(nèi)容、文本的變化
characterDataOldValue: true, // 觀察characterData變動時,是否需要記錄變動前的屬性值
childList: true, // 子結(jié)點(diǎn)變化
subtree: true, // 所有后代節(jié)點(diǎn)
});
// 停止監(jiān)聽
mutationObserver.disconnect()
// 清除變動記錄
mutationObserver.takeRecords()
圖片水印
實(shí)現(xiàn)思路
方案一:通過oss添加水印 方案二:通過canvas生成帶有水印的圖片
oss實(shí)現(xiàn)
oss方式不做過多描述了 簡單來說就是通過在獲取圖片時,在圖片鏈接上增加參數(shù),讓oss生成一張帶水印的圖片。 注意點(diǎn):
png圖片的透明區(qū)域無法被添加水印。 解決方式: 可通過添加參數(shù)的方式,讓oss將圖片轉(zhuǎn)為jpg格式(jpg格式會對透明區(qū)域做顏色填充)。
字體大小寫為定值,原圖大小會影響到水印字體的顯示大小。 解決方式:通過創(chuàng)建img標(biāo)簽,onLoad獲取圖片后,根據(jù)圖片寬高計算合適的字體大小,然后再一次獲取帶水印的圖片。
用戶通過刪除參數(shù)的方式可以刪除水印 解決方式:設(shè)置oss的安全級別,不帶水印不可訪問。
canvas實(shí)現(xiàn)
- 將img轉(zhuǎn)為canvas
async function imgToCanvas(cav, imgSrc) {
const img = new Image();
img.src = imgSrc;
// 防止因跨域?qū)е碌膱D片加載失敗(該方法有局限性)
img.setAttribute("crossOrigin", "anonymous");
// 等待圖片加載
await new Promise(resolve => (img.onload = resolve));
cav.width = img.width;
cav.height = img.height;
const ctx = cav.getContext("2d");
if (ctx) {
ctx.drawImage(img, 0, 0);
}
return cav;
}
- 添加水印
function addWaterMask(cav, content) {
const ctx = cav.getContext("2d");
ctx.fillStyle = "rgba(100,100,100,0.2)";
ctx.font = `24px serif`;
ctx.translate(0, 0);
ctx.rotate((5 * Math.PI) / 180);
// 生成水印
let x = 0, y = 0;
while (x < cav.width) {
y = 0;
while (y < cav.height) {
ctx.fillText(content, x, y);
y += 100;
}
x += 150;
}
}
- 使用
(async function () {
const canvas = document.createElement("canvas");
await imgToCanvas(
canvas,
"https://pp.myapp.com/ma_pic2/0/shot_54360764_1_1716462139/0"
);
addWaterMask(canvas, "介四sui印");
// document.body.appendChild(canvas);
return canvas.toDataUrl("image/png")
})();
到此這篇關(guān)于關(guān)于JS前端實(shí)現(xiàn)水印的代碼操作的文章就介紹到這了,更多相關(guān)JS實(shí)現(xiàn)水印內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何用RxJS實(shí)現(xiàn)Redux Form
這篇文章主要介紹了如何用RxJS實(shí)現(xiàn)Redux Form,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12
js獲取URL的參數(shù)的方法(getQueryString)示例
getQueryString方法默認(rèn)返回的是 string如果是int類型,則JS使用的時候,要進(jìn)行轉(zhuǎn)換一下,下面有個不錯的示例,大家可以參考下2013-09-09
JavaScript中的for...of和for...in循環(huán)容易遇到的問題及解決方法總結(jié)
在 JavaScript 編程中,for...of 和 for...in 是常用的循環(huán)語法,但它們在使用時可能會引發(fā)一些意想不到的問題,本文將分享我在使用這兩種循環(huán)時所遇到的坑和經(jīng)驗(yàn),需要的朋友可以參考下2023-08-08

