92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线

前端基于DOM或者Canvas實現頁面水印

2023-2-13    前端達人

前言

我們會看到很多頁面帶有水印,但是怎么實現呢?當然可以有多種實現方式,本文主要講解在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) { //如果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;`); // 創建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 } 
  • 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); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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'); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6. 缺點

  • 直接刪除水印元素時,頁面中的水印直接就被刪除了,當然我們可以用MutationObserver對水印元素進行監聽,刪除時,我們再立即生成一個水印元素就可以了,具體方面在下面講解。
  • 如果原始元素本身存在 css 定位等規則,會導致整體布局效果出現影響,因為上面實現排除了原始元素沒有定位,所以實現方式不是很嚴謹,本文具體實現實現如下:
    • 創建一個水印的容器設置為 position:relative
    • 將原有的節點放入到這個容器中
    • 同時創建一個帶有水印的 dom 設置為position:absolute ,實現這個水印元素覆蓋到原始元素的上層,以實現水印的效果。

三、基于Canvas和MutationObserver的實現方式

1. 思路整理

  • 配置水印的具體樣式(大小,旋轉角度,文字填充)
  • 設置水印(位置)
  • 監聽dom變化(防止水印刪除后頁面不再展示水?。?

2. 生成水印

通過將圖片繪制在cavans中,然后通過cavanstoDataURL方法,將圖片轉為base64編碼。

// 全局保存 canvas 和 div ,避免重復創建(單例模式) 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"); }; 
  • 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對比,不等的話,重新賦值
// 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); // 創建 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); }; // 監聽 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.監聽到被刪除元素的dom //   如果他兩相等的話就停止觀察,初始化(設置水印+啟動監控) //   (2) 刪除style中的屬性 //  1 判斷刪除的是否是標簽的屬性 (type === "attributes") //  2.判斷刪除的標簽屬性是否是在設置水印的標簽上 //  3.判斷修改過的style和之前的style對比,不等的話,重新賦值 if (removedNodes[0] === waterMarkEl) { console.log(removedNodes[0]) // 停止觀察。調用該方法后,DOM 再發生變動,也不會觸發觀察器。 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) { //注意img有onload的方法,如果自定義指令注冊在html標簽的話,只需要init(el, binding.value) 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咨詢、用戶體驗公司、軟件界面設計公司

日歷

鏈接

個人資料

藍藍設計的小編 http://www.skdbbs.com

存檔

92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线
亚洲精品久久视频| 午夜伦理精品一区| 色综合影院在线| 韩国欧美亚洲国产| 国产女人精品视频| 日本国产欧美一区二区三区| 午夜免费久久久久| 欧美激情精品久久久| 欧美日韩国产在线看| 亚洲人成网在线播放| 欧美一级大胆视频| 欧美亚洲视频在线观看| 欧美日韩一区二区在线| 6080yy精品一区二区三区| 国产精品一区二区三区在线播放| 日韩欧美亚洲一二三区| 2019中文字幕在线观看| 亚洲无av在线中文字幕| 国产精品青草久久久久福利99| 精品亚洲一区二区三区| 欧美成人精品在线视频| 国产精品美女午夜av| 91av视频在线观看| 日韩精品高清在线| 日韩中文字幕在线精品| 亚洲男人的天堂网站| 一区二区日韩精品| 日韩美女主播视频| 欧美日韩国产综合新一区| 亚洲综合在线做性| 欧美日韩国产麻豆| 青青草原成人在线视频| 国产精品狼人色视频一区| 欧美巨大黑人极品精男| 国产精品日韩欧美大师| 欧美日韩国产在线播放| 中文字幕少妇一区二区三区| 精品国产一区二区三区四区在线观看| 国产精品黄色av| 国产精品视频999| 欧美激情中文字幕乱码免费| 亚洲天堂第二页| 久久福利视频网| 欧美精品手机在线| 亚洲欧美国内爽妇网| 成人在线一区二区| 国产欧美日韩综合精品| 亚洲一区亚洲二区亚洲三区| 亚洲级视频在线观看免费1级| 欧美尤物巨大精品爽| 97香蕉超级碰碰久久免费软件| 久久久精品电影| 成人在线国产精品| xxx成人少妇69| 久久精品久久久久| 在线精品国产成人综合| 精品国产一区二区三区久久久| 欧美精品18videos性欧| 国产精品99久久久久久白浆小说| 亚洲成av人乱码色午夜| 俺去了亚洲欧美日韩| 欧美性猛交xxxx富婆| 欧美日韩成人精品| 国产成人福利网站| 韩国19禁主播vip福利视频| 欧美xxxx做受欧美.88| 亚洲18私人小影院| 日本精品在线视频| 亚洲深夜福利网站| 国产啪精品视频网站| 97视频在线观看免费| 狠狠躁夜夜躁人人躁婷婷91| www.精品av.com| 精品亚洲一区二区三区在线播放| 国产精品日韩一区| 欧美大尺度在线观看| 亚洲情综合五月天| 91老司机精品视频| 亚洲天堂av电影| 国产成人精品优优av| 在线观看成人黄色| 国产精品高潮在线| 96pao国产成视频永久免费| 色综合久久88| 亚洲精品一区二区三区不| 亚洲视频欧洲视频| 久久青草精品视频免费观看| 国产在线不卡精品| 久久久久久久电影一区| 97精品国产97久久久久久免费| 国产精品a久久久久久| 成人激情视频小说免费下载| 久久影院免费观看| 亚洲精品日韩激情在线电影| 成人高清视频观看www| 2019日本中文字幕| 日韩av中文字幕在线免费观看| 欧美精品videosex牲欧美| 久久亚洲精品中文字幕冲田杏梨| 亚洲色图13p| 日本sm极度另类视频| 性欧美xxxx| 亚洲国产美女久久久久| 国产伦精品一区二区三区精品视频| 一区二区欧美亚洲| 欧美性猛交丰臀xxxxx网站| 91国语精品自产拍在线观看性色| 4388成人网| 国产精品小说在线| 亚洲性视频网站| 亚洲娇小xxxx欧美娇小| 久久香蕉频线观| 日韩亚洲第一页| 国产亚洲a∨片在线观看| 91最新在线免费观看| 欧美成人精品三级在线观看| 欧美激情2020午夜免费观看| 秋霞av国产精品一区| 国产亚洲视频中文字幕视频| 国产一区二区在线免费| 亚洲精品一二区| 欧美激情免费在线| 精品中文字幕视频| 欧美专区中文字幕| 91丨九色丨国产在线| 欧美成人精品h版在线观看| 久久中国妇女中文字幕| 91爱爱小视频k| 亚洲另类图片色| 97在线精品视频| 久久视频在线观看免费| 日韩69视频在线观看| 黄色精品在线看| 成人在线观看视频网站| 欧美日韩电影在线观看| 日韩av手机在线| 日韩av大片在线| 久久综合网hezyo| 亚洲天堂免费在线| 国产成人欧美在线观看| 亚洲黄色www网站| 国产精品美女无圣光视频| 国产日韩综合一区二区性色av| 97精品欧美一区二区三区| 国产精品视频自在线| 最近2019中文字幕在线高清| 欧美在线一级va免费观看| 久久精品国亚洲| 国产精品免费看久久久香蕉| 国产精品激情av电影在线观看| 高清欧美性猛交xxxx| 亚洲综合成人婷婷小说| 精品日韩视频在线观看| 91精品久久久久久久久久另类| 久久成年人视频| www.亚洲一区| 亚洲午夜性刺激影院| 成人黄色网免费| 亚洲午夜色婷婷在线| 日本精品性网站在线观看| 青青久久av北条麻妃海外网| 亚洲欧美福利视频| 69久久夜色精品国产7777| 亚洲欧美中文在线视频|