JavaScript實(shí)現(xiàn)水印效果的示例代碼
效果
實(shí)現(xiàn)思路
- 利用canvas繪制出文字
- 將canvas作為遮罩層背景圖, 將背景x軸和y軸重復(fù)
實(shí)現(xiàn)步驟
動(dòng)態(tài)生成canvas并畫(huà)出文字
const canvas = document.createElement("canvas"); canvas.width = len * fontSize; // canvas寬度, 目前是根據(jù)文字長(zhǎng)度和大小來(lái)調(diào)整的, 自己可依照具體需求變動(dòng) canvas.height = height + fontSize * 2.8; // canvas高度, 依據(jù)需求調(diào)整 const context = canvas.getContext("2d"); context.translate(0, canvas.height / 2); // 改變旋轉(zhuǎn)基點(diǎn) context.rotate((-rotate * Math.PI) / 180); // 進(jìn)行旋轉(zhuǎn), 傳過(guò)來(lái)的旋轉(zhuǎn)角度 context.font = `${fontSize}px Vedana`; // 設(shè)置字體 context.fillStyle = color; // 設(shè)置文字顏色 // 將需要的文本, 繪制到canvas上面 context.fillText(text, 10, canvas.height / 2 - 100);
將canvas做為遮罩層背景圖
// 生成水印遮罩層 const div = document.createElement("div"); div.id = DOM_ID; div.style.pointerEvents = "none"; div.style.position = "fixed"; div.style.zIndex = zIndex; div.style.left = "-32%"; div.style.top = "-32%"; div.style.opacity = opacity; div.style.width = "150%"; div.style.height = "150%"; div.style.background = `url('${canvas.toDataURL("images/png")}')repeat left top`; document.body.appendChild(div);
防止篡改水印
利用MutationObserver
API來(lái)對(duì)遮罩層做監(jiān)聽(tīng), 防止屬性修改或者dom節(jié)點(diǎn)被人為的刪除
MDN: MutationObserver
/** * 監(jiān)聽(tīng)dom變化, 防止水印被篡改 */ static observeDomChange = (waterMarkDom, options) => { const callback = (mutationsList, observer) => { for (const mutation of mutationsList) { /** * 水印節(jié)點(diǎn)的屬性發(fā)生了變動(dòng) */ if (mutation.target === waterMarkDom) { this.setWaterMark(); // 重新生成水印 observer.disconnect(); // 停止觀察 } /** * 強(qiáng)行手動(dòng)刪除了水印節(jié)點(diǎn) */ if (mutation.removedNodes.length && mutation.removedNodes[0] === waterMarkDom) { this.setWaterMark(this.options); // 重新生成水印 observer.disconnect(); // 停止觀察 } } }; this.observer = new MutationObserver(callback); /** 監(jiān)聽(tīng)body */ this.observer.observe(document.querySelector("body"), { attributes: true, // 觀察屬性變動(dòng) childList: true, // 觀察目標(biāo)子節(jié)點(diǎn)的變化,是否有添加或者刪除 subtree: true, // 觀察后代節(jié)點(diǎn),默認(rèn)為 false }); };
所有代碼
const DOM_ID = "yss-cj-create"; /** * 水印的默認(rèn)屬性 */ const DEFAULT_OPTIONS = { text: "cxk 管理員 20230424", width: 520, // 水印塊的寬度 height: 280, // 水印塊的高度 rotate: 20, // 水印塊的旋轉(zhuǎn)角度 fontSize: 28, // 文字大小 color: "#666", // 文字顏色 opacity: "0.3", // 遮罩層的透明度 zIndex: "9999999999", // 遮罩層的層級(jí) }; class Watermark { options = {}; observer = null; /** * 生成水印 */ static setWaterMark = (options = {}) => { const waterDom = document.getElementById(DOM_ID); if (waterDom !== null) { // 每次重新繪制之前, 需要判斷是否已經(jīng)存在, 如果存在了就先刪除, 再來(lái)重新繪制 document.body.removeChild(waterDom); } const latestOptions = { ...DEFAULT_OPTIONS, ...options }; this.options = latestOptions; const { text, width, // 寬度是根據(jù)提供的文字大小和文字長(zhǎng)度計(jì)算出來(lái)的, 這里就用不上了 height, // 水印塊的高度 rotate, // 水印塊的旋轉(zhuǎn)角度 fontSize, // 文字大小 color, // 文字顏色 opacity, // 遮罩層的透明度 zIndex, // 遮罩層的層級(jí) } = latestOptions; const len = text.length; const canvas = document.createElement("canvas"); canvas.width = len * fontSize; canvas.height = height + fontSize * 2.8; const context = canvas.getContext("2d"); context.translate(0, canvas.height / 2); context.rotate((-rotate * Math.PI) / 180); context.font = `${fontSize}px Vedana`; // 設(shè)置字體 context.fillStyle = color; // 設(shè)置文字顏色 // 將需要的文本, 繪制到canvas上面 context.fillText(text, 10, canvas.height / 2 - 100); // 生成水印遮罩層 const div = document.createElement("div"); div.id = DOM_ID; div.style.pointerEvents = "none"; div.style.position = "fixed"; div.style.zIndex = zIndex; div.style.left = "-32%"; div.style.top = "-32%"; div.style.opacity = opacity; div.style.width = "150%"; div.style.height = "150%"; div.style.background = `url('${canvas.toDataURL("images/png")}')repeat left top`; document.body.appendChild(div); /** * 監(jiān)聽(tīng)水印的dom變化 */ this.observeDomChange(div); }; /** * 去除水印 */ static removeWatermark = () => { const dom = document.getElementById(DOM_ID); if (dom !== null) { document.body.removeChild(dom); } }; /** * 監(jiān)聽(tīng)dom變化, 防止水印被篡改 */ static observeDomChange = (waterMarkDom, options) => { const callback = (mutationsList, observer) => { for (const mutation of mutationsList) { /** * 水印節(jié)點(diǎn)的屬性發(fā)生了變動(dòng) */ if (mutation.target === waterMarkDom) { this.setWaterMark(); // 重新生成水印 observer.disconnect(); // 停止觀察 } /** * 強(qiáng)行手動(dòng)刪除了水印節(jié)點(diǎn) */ if (mutation.removedNodes.length && mutation.removedNodes[0] === waterMarkDom) { this.setWaterMark(this.options); // 重新生成水印 observer.disconnect(); } } }; this.observer = new MutationObserver(callback); /** 監(jiān)聽(tīng)body */ this.observer.observe(document.querySelector("body"), { attributes: true, // 觀察屬性變動(dòng) childList: true, // 觀察目標(biāo)子節(jié)點(diǎn)的變化,是否有添加或者刪除 subtree: true, // 觀察后代節(jié)點(diǎn),默認(rèn)為 false }); }; } Watermark.setWaterMark();
到此這篇關(guān)于JavaScript實(shí)現(xiàn)水印效果的示例代碼的文章就介紹到這了,更多相關(guān)JavaScript水印內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS實(shí)現(xiàn)移動(dòng)端觸屏拖拽功能
這篇文章主要介紹了JS實(shí)現(xiàn)移動(dòng)端觸屏拖拽功能,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-07-07初學(xué)js插入節(jié)點(diǎn)appendChild insertBefore使用方法
由于可見(jiàn)insertBefore()方法的特性是在已有的子節(jié)點(diǎn)前面插入新的節(jié)點(diǎn)但是兩種情況結(jié)合起來(lái)發(fā)現(xiàn)insertBefore()方法插入節(jié)點(diǎn),是可以在子節(jié)點(diǎn)列表的任意位置。2011-07-07uniapp小程序開(kāi)發(fā)組件封裝之自定義輪播圖效果
這篇文章主要介紹了uniapp小程序開(kāi)發(fā)組件封裝之自定義輪播圖,本文主要展示小程序端封裝輪播圖組件,使用的是uniapp進(jìn)行的開(kāi)發(fā),主要使用的是uniapp官網(wǎng)提供的swiper組件,需要的朋友可以參考下2023-02-02javascript 判斷用戶有沒(méi)有操作頁(yè)面
這篇文章主要介紹了javascript 判斷用戶有沒(méi)有操作頁(yè)面的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-10-10JavaScript 隨機(jī)驗(yàn)證碼的生成實(shí)例代碼
這篇文章主要介紹了JavaScript 隨機(jī)驗(yàn)證碼的生成實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-09-09