前言
我們會看到很多頁面帶有水印,但是怎么實現呢?當然可以有多種實現方式,本文主要講解在vue項目中基于DOM或者Cavans實現水印效果,當然還有其他的實現方式,比如在原圖片的基礎上加上水印生成新的圖片,但是這需要后端處理。因為要在vue項目中使用,所以我使用自定義指令可以直接對掛載的dom實現水印效果。
本文實現水印的項目環境為:vue + vite + ts
一、vue自定義指令directive講解
前面專門有一篇講解vue2.x與vue3.x中自定義指令詳解
二、基于DOM的實現方式
1. 思路整理
-
獲取寬高
(1)獲取綁定元素的實際寬度clientWidth
(2)獲取綁定元素實際高度clientHeight
(3)獲取綁定元素的父元素parentElement
-
創建盒子
(1)創建一個包裹水印圖片的盒子
(2)創建一個水印圖片的盒子
-
設置盒子樣式
(1)包裹水印盒子寬高為綁定元素的寬高,即clientWidth、clientHeight
(2)水印盒子設置背景圖、旋轉度、寬高、點擊穿透
-
設置創建的元素的位置
(1)水印盒子放到包裹水印圖片的盒子里 (包裹水印圖片的盒子包裹水印)
(2)包裹水印圖片的盒子放到被綁定元素之前
(3)被綁定元素放到裹水印圖片的盒子里(不然被綁定元素與包裹水印圖片的盒子層級同級)
2.新建index.vue
將水印的指令放到標簽上,設置標簽的寬高。水印可以放大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>
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
3. 新建directives文件
在directives文件下創建waterMark.ts 文件,具體內容實現如下:
import waterImg from "@/assets/vue.svg" const directives: any = { mounted(el: HTMLElement) { const { clientWidth, clientHeight, parentElement } = el; console.log(parentElement, 'parentElement') const waterMark: HTMLElement = document.createElement('div'); const waterBg: HTMLElement = document.createElement('div'); waterMark.className = `water-mark`; waterMark.setAttribute('style', ` display: inline-block;
overflow: hidden;
position: relative;
width: ${clientWidth}px;
height: ${clientHeight}px;`); 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; `); waterMark.appendChild(waterBg); parentElement?.insertBefore(waterMark, el); waterMark.appendChild(el); } } export default { name: 'watermark', directives }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
4. 在directives文件下創建 index.ts文件
import type { App } from 'vue' import watermark from './waterMark' export default function installDirective(app: App) { app.directive(watermark.name, watermark.directives); }
5. 在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');
6. 缺點
-
直接刪除水印元素時,頁面中的水印直接就被刪除了,當然我們可以用
MutationObserver對水印元素進行監聽,刪除時,我們再立即生成一個水印元素就可以了,具體方面在下面講解。
-
如果原始元素本身存在 css 定位等規則,會導致整體布局效果出現影響,因為上面實現排除了原始元素沒有定位,所以實現方式不是很嚴謹,本文具體實現實現如下:
-
創建一個水印的容器設置為
position:relative
-
將原有的節點放入到這個容器中
-
同時創建一個帶有水印的 dom 設置為
position:absolute ,實現這個水印元素覆蓋到原始元素的上層,以實現水印的效果。
三、基于Canvas和MutationObserver的實現方式
1. 思路整理
-
配置水印的具體樣式(大小,旋轉角度,文字填充)
-
設置水印(位置)
-
監聽dom變化(防止水印刪除后頁面不再展示水?。?
2. 生成水印
通過將圖片繪制在cavans中,然后通過cavans的toDataURL方法,將圖片轉為base64編碼。
const globalCanvas = null; const globalWaterMark = null; const getDataUrl = ( ) => { 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"); };
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
3. 使用MutationObserver監聽水印
使用MutationObserver監聽dom變化,MutationObserver詳細用法之前已經講過了,詳細可見作為前端你還不懂MutationObserver?那Out了
具體監聽邏輯如下:
-
1.直接刪除dom
(1)先獲取設置水印的dom
(2)監聽到被刪除元素的dom
(3)如果他兩相等的話就停止觀察,初始化(設置水印+啟動監控)
-
2.刪除style中的屬性
(1)判斷刪除的是否是標簽的屬性 (type === “attributes”)
(2)判斷刪除的標簽屬性是否是在設置水印的標簽上
(3)判斷修改過的style和之前的style對比,不等的話,重新賦值
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; const url = getDataUrl(binding); 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;"); parentElement.appendChild(waterMark); }; const createObserver = (el: HTMLElement, binding: any) => { console.log(el, 'el') console.log(style, 'style') 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"); if (removedNodes[0] === waterMarkEl) { console.log(removedNodes[0]) observer.disconnect(); 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); createObserver(el, binding.value); }; const directives: any = { mounted(el: HTMLElement, binding: any) { el.onload = init.bind(null, el, binding.value); }, };
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
四、成果展示
刪除水印標簽依然還在,除非刪除水印注冊的標簽才能刪除水印,但是這樣做毫無意義,因為這樣做內容也會全部刪除掉。
附:文中用到的js基礎知識
toDataURL用法
toDataURL(type, encoderOptions),接收兩個參數:
-
type:圖片類型,比如
image/png、image/jpeg、image/webp等等,默認為image/png格式
-
encoderOptions:圖片質量的取值范圍(0-1),默認值為0.92,當超出界限按默認值0.92
藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請加藍小助,微信號:ben_lanlan,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系01063334945。
分享此文一切功德,皆悉回向給文章原作者及眾讀者.
免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。
藍藍設計( www.skdbbs.com )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務、UI設計公司、界面設計公司、UI設計服務公司、數據可視化設計公司、UI交互設計公司、高端網站設計公司、UI咨詢、用戶體驗公司、軟件界面設計公司