關(guān)于JS前端實(shí)現(xiàn)水印的代碼操作
網(wǎng)頁(yè)水印
實(shí)現(xiàn)思路
- 通過(guò)canvas生成一張水印圖片
- 通過(guò)css將圖片設(shè)置為目標(biāo)節(jié)點(diǎn)的背景圖
- 通過(guò)MutationObserver監(jiān)聽(tīng)目標(biāo)節(jié)點(diǎn)的類(lèi)名變化,防止水印被刪除
代碼操作
- 通過(guò)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è)置畫(huà)筆的方向 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)); // 縱向拉開(kāi)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, }); // 通過(guò)設(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)聽(tīng)節(jié)點(diǎn)的變化
function listenerDOMChange(className) { // 獲取要監(jiān)聽(tīng)的節(jié)點(diǎn) const targetNode = document.querySelector(`.${className}`); // 創(chuàng)建監(jiān)聽(tīng)器 const observer = new MutationObserver(mutationList => { // 遍歷變化記錄 for (let mutationRecord of mutationList) { // 如果目標(biāo)節(jié)點(diǎn)的class屬性發(fā)生變化,判斷是不是類(lèi)名被刪了,是的話(huà)把類(lèi)名加回去 if (mutationRecord.attributeName === "class") { if(!Array.from(targetNode.classList).includes(className)) { targetNode.classList.add(className) } } } }); // 啟動(dòng)監(jiān)聽(tīng) observer.observe(targetNode, { attributes: true, }); } function getWaterMark({ content, className, canvasHeight = 140, canvasWidth = 150, }) { // 監(jiān)聽(tīng) 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 用來(lái)監(jiān)聽(tīng)DOM的變化,DOM的增刪、DOM屬性的變化,子結(jié)點(diǎn)和文本內(nèi)容的變化,都可以被監(jiān)聽(tīng)。
MutationObserver 的監(jiān)聽(tīng)和事件不同,事件是同步的,DOM的變化會(huì)立即觸發(fā)對(duì)應(yīng)的事件,而 MutationObserver 是異步的,會(huì)在下一個(gè)微任務(wù)執(zhí)行時(shí)觸發(fā)監(jiān)聽(tīng)回調(diào)。
- 創(chuàng)建MutationObserver
const observer = new MutationObserver((mutationsList, observer) => { // mutationsList mutationRecord數(shù)組 記錄了DOM的變化 // observer MutationObserver的實(shí)例 // 監(jiān)聽(tīng)回調(diào) console.log(mutationsList, observer); }) mutationObserver.observe(document.documentElement, { attributes: true, characterData: true, childList: true, subtree: true, attributeOldValue: true, characterDataOldValue: true });
- 開(kāi)啟監(jiān)聽(tīng)
// node 監(jiān)聽(tīng)的節(jié)點(diǎn) // config 監(jiān)聽(tīng)配置(要監(jiān)聽(tīng)哪些內(nèi)容) // observer.observe(node, config); mutationObserver.observe(document.documentElement, { attributes: true, // 屬性變化 attributeOldValue: true, // 觀(guān)察attributes變動(dòng)時(shí),是否需要記錄變動(dòng)前的屬性值 attributeFilter: [‘class',‘src'] // 需要觀(guān)察的特定屬性 characterData: true, // 節(jié)點(diǎn)內(nèi)容、文本的變化 characterDataOldValue: true, // 觀(guān)察characterData變動(dòng)時(shí),是否需要記錄變動(dòng)前的屬性值 childList: true, // 子結(jié)點(diǎn)變化 subtree: true, // 所有后代節(jié)點(diǎn) }); // 停止監(jiān)聽(tīng) mutationObserver.disconnect() // 清除變動(dòng)記錄 mutationObserver.takeRecords()
圖片水印
實(shí)現(xiàn)思路
方案一:通過(guò)oss添加水印 方案二:通過(guò)canvas生成帶有水印的圖片
oss實(shí)現(xiàn)
oss方式不做過(guò)多描述了 簡(jiǎn)單來(lái)說(shuō)就是通過(guò)在獲取圖片時(shí),在圖片鏈接上增加參數(shù),讓oss生成一張帶水印的圖片。 注意點(diǎn):
png圖片的透明區(qū)域無(wú)法被添加水印。 解決方式: 可通過(guò)添加參數(shù)的方式,讓oss將圖片轉(zhuǎn)為jpg格式(jpg格式會(huì)對(duì)透明區(qū)域做顏色填充)。
字體大小寫(xiě)為定值,原圖大小會(huì)影響到水印字體的顯示大小。 解決方式:通過(guò)創(chuàng)建img標(biāo)簽,onLoad獲取圖片后,根據(jù)圖片寬高計(jì)算合適的字體大小,然后再一次獲取帶水印的圖片。
用戶(hù)通過(guò)刪除參數(shù)的方式可以刪除水印 解決方式:設(shè)置oss的安全級(jí)別,不帶水印不可訪(fǎng)問(wèn)。
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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何用RxJS實(shí)現(xiàn)Redux Form
這篇文章主要介紹了如何用RxJS實(shí)現(xiàn)Redux Form,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12js獲取URL的參數(shù)的方法(getQueryString)示例
getQueryString方法默認(rèn)返回的是 string如果是int類(lèi)型,則JS使用的時(shí)候,要進(jìn)行轉(zhuǎn)換一下,下面有個(gè)不錯(cuò)的示例,大家可以參考下2013-09-09js實(shí)現(xiàn)簡(jiǎn)單的購(gòu)物車(chē)有圖有代碼
這篇文章主要介紹了用js實(shí)現(xiàn)的簡(jiǎn)單購(gòu)物車(chē),配有截圖,適合初學(xué)者2014-05-05JavaScript中的for...of和for...in循環(huán)容易遇到的問(wèn)題及解決方法總結(jié)
在 JavaScript 編程中,for...of 和 for...in 是常用的循環(huán)語(yǔ)法,但它們?cè)谑褂脮r(shí)可能會(huì)引發(fā)一些意想不到的問(wèn)題,本文將分享我在使用這兩種循環(huán)時(shí)所遇到的坑和經(jīng)驗(yàn),需要的朋友可以參考下2023-08-08