2023-2-13 前端達人
我們會看到很多頁面帶有水印,但是怎么實現(xiàn)呢?當然可以有多種實現(xiàn)方式,本文主要講解在vue項目中基于DOM或者Cavans實現(xiàn)水印效果,當然還有其他的實現(xiàn)方式,比如在原圖片的基礎上加上水印生成新的圖片,但是這需要后端處理。因為要在vue項目中使用,所以我使用自定義指令可以直接對掛載的dom實現(xiàn)水印效果。
本文實現(xiàn)水印的項目環(huán)境為:vue + vite + ts
前面專門有一篇講解vue2.x與vue3.x中自定義指令詳解
將水印的指令放到標簽上,設置標簽的寬高。水印可以放大div
標簽上,也可以是img
標簽上。注意:img
才有onload
方法,div
標簽么有。
<script setup lang="ts"> import { ref } from "vue"; </script> <template> <div class="index-content" > <div class="watermaker" v-watermark ></div> <!-- <img v-watermark style="width:400px;height:400px" src="../assets/vue.svg" alt=""> --> </div> </template> <style scoped> .watermaker { width: 400px; height: 400px; } .index-content{ width: 100%; height: 100%; } </style>
![]()
directives
文件
在directives
文件下創(chuàng)建waterMark.ts
文件,具體內容實現(xiàn)如下:
import waterImg from "@/assets/vue.svg" const directives: any = { mounted(el: HTMLElement) { //如果el元素是img,則可以用el.onload將下面包裹 const { clientWidth, clientHeight, parentElement } = el; console.log(parentElement, 'parentElement') const waterMark: HTMLElement = document.createElement('div'); const waterBg: HTMLElement = document.createElement('div'); //設置waterMark的class和style waterMark.className = `water-mark`; waterMark.setAttribute('style', ` display: inline-block; overflow: hidden; position: relative; width: ${clientWidth}px; height: ${clientHeight}px;`); // 創(chuàng)建waterBg的class和style waterBg.className = `water-mark-bg`;// 方便自定義展示結果 waterBg.setAttribute('style', ` position: absolute; pointer-events: none;`在這里插入代碼片` transform: rotate(45deg); width: 100%; height: 100%; opacity: 0.2; background-image: url(${waterImg}); background-repeat: repeat; `); // 水印元素waterBg放到waterMark元素中 waterMark.appendChild(waterBg); //waterMark插入到el之前,即插入到綁定元素之前 parentElement?.insertBefore(waterMark, el); // 綁定元素移入到包裹水印的盒子 waterMark.appendChild(el); } } export default { name: 'watermark', directives }
![]()
directives
文件下創(chuàng)建 index.ts
文件
import type { App } from 'vue' import watermark from './waterMark' export default function installDirective(app: App) { app.directive(watermark.name, watermark.directives); }
main.ts
中全局引入
import { createApp } from 'vue' import App from './App.vue' import directives from './directives' const app = createApp(App); app.use(directives); app.mount('#app');
MutationObserver
對水印元素進行監(jiān)聽,刪除時,我們再立即生成一個水印元素就可以了,具體方面在下面講解。
position:relative
position:absolute
,實現(xiàn)這個水印元素覆蓋到原始元素的上層,以實現(xiàn)水印的效果。
通過將圖片繪制在cavans
中,然后通過cavans
的toDataURL
方法,將圖片轉為base64編碼。
// 全局保存 canvas 和 div ,避免重復創(chuàng)建(單例模式) const globalCanvas = null; const globalWaterMark = null; // 獲取 toDataURL 的結果 const getDataUrl = ( // font = "16px normal", // fillStyle = "rgba(180, 180, 180, 0.3)", // textAlign, // textBaseline, // text = "請勿外傳", ) => { const rotate = -10; const canvas = globalCanvas || document.createElement("canvas"); const ctx = canvas.getContext("2d"); // 獲取畫布上下文 ctx.rotate((rotate * Math.PI) / 180); ctx.font = "16px normal"; ctx.fillStyle = "rgba(180, 180, 180, 0.3)"; ctx.textAlign = "left"; ctx.textBaseline = "middle"; ctx.fillText('請勿外傳', canvas.width / 3, canvas.height / 2); return canvas.toDataURL("image/png"); };
![]()
使用MutationObserver
監(jiān)聽dom變化,MutationObserver
詳細用法之前已經講過了,詳細可見作為前端你還不懂MutationObserver?那Out了
具體監(jiān)聽邏輯如下:
// watermark 樣式 let style = ` display: block; overflow: hidden; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-repeat: repeat; pointer-events: none;`; //設置水印 const setWaterMark = (el: HTMLElement, binding: any) => { const { parentElement } = el; // 獲取對應的 canvas 畫布相關的 base64 url const url = getDataUrl(binding); // 創(chuàng)建 waterMark 父元素 const waterMark = globalWaterMark || document.createElement("div"); waterMark.className = `water-mark`; // 方便自定義展示結果 style = `${style}background-image: url(${url});`; waterMark.setAttribute("style", style); // 將對應圖片的父容器作為定位元素 parentElement.setAttribute("style", "position: relative;"); // 將圖片元素移動到 waterMark 中 parentElement.appendChild(waterMark); }; // 監(jiān)聽 DOM 變化 const createObserver = (el: HTMLElement, binding: any) => { console.log(el, 'el') console.log(style, 'style') // console.log(el.parentElement.querySelector('.water-mark'),'el.parentElement') const waterMarkEl = el.parentElement.querySelector(".water-mark"); const observer = new MutationObserver((mutationsList) => { console.log(mutationsList, 'mutationsList') if (mutationsList.length) { const { removedNodes, type, target } = mutationsList[0]; const currStyle = waterMarkEl.getAttribute("style"); // console.log(currStyle, 'currStyle') // 證明被刪除了 // (1)直接刪除dom // 1.先獲取設置水印的dom // 2.監(jiān)聽到被刪除元素的dom // 如果他兩相等的話就停止觀察,初始化(設置水印+啟動監(jiān)控) // (2) 刪除style中的屬性 // 1 判斷刪除的是否是標簽的屬性 (type === "attributes") // 2.判斷刪除的標簽屬性是否是在設置水印的標簽上 // 3.判斷修改過的style和之前的style對比,不等的話,重新賦值 if (removedNodes[0] === waterMarkEl) { console.log(removedNodes[0]) // 停止觀察。調用該方法后,DOM 再發(fā)生變動,也不會觸發(fā)觀察器。 observer.disconnect(); //初始化(設置水印,啟動監(jiān)控) init(el, binding); } else if ( type === "attributes" && target === waterMarkEl && currStyle !== style ) { console.log(currStyle, 'currStyle') console.log(style, 'style') waterMarkEl.setAttribute("style", style); } } }); observer.observe(el.parentElement, { childList: true, attributes: true, subtree: true, }); }; // 初始化 const init = (el: HTMLElement, binding: any = {}) => { // 設置水印 setWaterMark(el, binding.value); // 啟動監(jiān)控 createObserver(el, binding.value); }; const directives: any = { mounted(el: HTMLElement, binding: any) { //注意img有onload的方法,如果自定義指令注冊在html標簽的話,只需要init(el, binding.value) el.onload = init.bind(null, el, binding.value); }, };
![]()
刪除水印標簽依然還在,除非刪除水印注冊的標簽才能刪除水印,但是這樣做毫無意義,因為這樣做內容也會全部刪除掉。
toDataURL(type, encoderOptions)
,接收兩個參數(shù):
image/png、image/jpeg、image/webp
等等,默認為image/png
格式
分享此文一切功德,皆悉回向給文章原作者及眾讀者.
免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯(lián)系,我們立即更正或刪除。
藍藍設計( www.yvirxh.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業(yè)提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務、UI設計公司、界面設計公司、UI設計服務公司、數(shù)據(jù)可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司
藍藍設計的小編 http://www.yvirxh.cn