關(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 是異步的,會在下一個(gè)微任務(wù)執(zhí)行時(shí)觸發(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變動時(shí),是否需要記錄變動前的屬性值 attributeFilter: [‘class',‘src'] // 需要觀察的特定屬性 characterData: true, // 節(jié)點(diǎn)內(nèi)容、文本的變化 characterDataOldValue: true, // 觀察characterData變動時(shí),是否需要記錄變動前的屬性值 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í),在圖片鏈接上增加參數(shù),讓oss生成一張帶水印的圖片。 注意點(diǎn):
png圖片的透明區(qū)域無法被添加水印。 解決方式: 可通過添加參數(shù)的方式,讓oss將圖片轉(zhuǎn)為jpg格式(jpg格式會對透明區(qū)域做顏色填充)。
字體大小寫為定值,原圖大小會影響到水印字體的顯示大小。 解決方式:通過創(chuàng)建img標(biāo)簽,onLoad獲取圖片后,根據(jù)圖片寬高計(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,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12js獲取URL的參數(shù)的方法(getQueryString)示例
getQueryString方法默認(rèn)返回的是 string如果是int類型,則JS使用的時(shí)候,要進(jìn)行轉(zhuǎn)換一下,下面有個(gè)不錯(cuò)的示例,大家可以參考下2013-09-09JavaScript中的for...of和for...in循環(huán)容易遇到的問題及解決方法總結(jié)
在 JavaScript 編程中,for...of 和 for...in 是常用的循環(huán)語法,但它們在使用時(shí)可能會引發(fā)一些意想不到的問題,本文將分享我在使用這兩種循環(huán)時(shí)所遇到的坑和經(jīng)驗(yàn),需要的朋友可以參考下2023-08-08