前端水印實(shí)現(xiàn)方案詳細(xì)舉例介紹
1. 背景
為了防止信息泄露或知識產(chǎn)權(quán)被侵犯,我們可以給數(shù)據(jù)或圖片加一層水印,水印能夠很好的保護(hù)知識產(chǎn)權(quán)。
2. 方式
水印的添加根據(jù)環(huán)境可以分為兩大類,前端瀏覽器環(huán)境添加和后端服務(wù)環(huán)境添加
前端實(shí)現(xiàn)的特點(diǎn)
- 可以不占用服務(wù)器資源,完全依賴客戶端的計(jì)算能力,減少服務(wù)端壓力
- 速度快,無論哪種前端的實(shí)現(xiàn)方式,性能都是優(yōu)于后端的
- 實(shí)現(xiàn)方式簡單
- 安全系數(shù)較低,對于掌握一定前端知識的人來說可以通過各種騷操作跳過水印獲取到源文件
后端實(shí)現(xiàn)的特點(diǎn)
- 安全,安全,安全
- 當(dāng)遇到大文件密集水印,或是復(fù)雜水印,占用服務(wù)器內(nèi)存、運(yùn)算量,請求時間過長
3. 前端實(shí)現(xiàn)方案
3.1 重復(fù)的dom元素覆蓋實(shí)現(xiàn)
- 在頁面上覆蓋一個蒙層,設(shè)置蒙層的透明度,設(shè)置pointer-events為none, 實(shí)現(xiàn)點(diǎn)擊穿透
- 生成一個水印片,上面是顯示水印的文字,我們可以給文字加一個旋轉(zhuǎn),和
userSelect屬。 性,讓此時的文字無法被選中 - 在蒙層上重復(fù)循環(huán)生成水印片
divWaterMark (width, height, content) {
const waterWrapper = document.createElement('div')
cssHelper(waterWrapper, {
position: 'fixed',
top: '0px',
right: '0px ',
bottom: '0px',
left: '0px',
overflow: 'hidden',
display: 'flex',
'flex-wrap': 'wrap',
'pointer-events': 'none'
})
const waterHeight = width
const waterWidth = height
const { clientWidth, clientHeight } = document.documentElement || document.body
const column = Math.ceil(clientWidth / waterWidth)
const rows = Math.ceil(clientHeight / waterHeight)
for (let i = 0; i < column * rows; i++) {
const wrap = document.createElement('div')
cssHelper(
wrap,
Object.create({
position: 'relative',
width: `${waterWidth}px`,
height: `${waterHeight}px`,
flex: `0 0 ${waterWidth}px`,
overflow: 'hidden'
})
)
wrap.appendChild(createItem(content))
waterWrapper.appendChild(wrap)
}
document.body.appendChild(waterWrapper)
},

3.2 canvas 實(shí)現(xiàn)方式
創(chuàng)建一個canvas畫布,繪制出一個水印區(qū)域,將這個水印通過toDataURL方法輸出為一個圖片,將這個圖片設(shè)置為蒙層的背景圖,通過圖片的backgroud-repeat:repeat;樣式實(shí)現(xiàn)填滿整個屏幕的效果
const createWaterMark = (width, height, content) => {
const angle = -20
const txt = content
const canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, width, height)
ctx.fillStyle = '#000'
ctx.globalAlpha = 0.1
ctx.font = `16px serif`
ctx.rotate((Math.PI / 180) * angle)
ctx.fillText(txt, 0, 50)
return canvas.toDataURL()
}
const watermakr = document.createElement('div')
watermakr.className = 'watermark'
watermakr.style.backgroundImage = `url(${createWaterMark()})`
document.body.appendChild(watermakr)
<style>
.watermark {
position: fixed;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
pointer-events: none;
background-repeat: repeat;
}
</style>

3.3 svg實(shí)現(xiàn)
svg 和 canvas 類似,只不過是生成背景圖的方法換成了通過svg生成
4. 水印破解
上面實(shí)現(xiàn)水印的方法,都存在一個問題很明顯的問題,那就是對于有些前端知識的人來說通過開發(fā)者調(diào)試工具稍微操作一下,就能夠?qū)е滤∈В?/p>
- 刪除對應(yīng)
dom節(jié)點(diǎn) - 設(shè)置對應(yīng)
dom節(jié)點(diǎn)的css樣式
5. 水印防御
MutationObserver
MutationObserver 是一個可以監(jiān)聽DOM結(jié)構(gòu)變化的接口,能夠監(jiān)聽 DOM 樹屬性、節(jié)點(diǎn)本身、子節(jié)點(diǎn) 等的變化。防御思路就是就是使用 MutationObserver 去監(jiān)聽外部對應(yīng) water-mark 節(jié)點(diǎn)的操作,只要監(jiān)聽到了就重新渲染水印效果即可。
主要觀察的有三點(diǎn)
- 水印元素本身是否被移除
- 水印元素屬性是否被篡改(display: none ...)
- 水印元素的子元素是否被移除和篡改 (element生成的方式 )
createObserver () {
// 創(chuàng)建一個觀察器實(shí)例并傳入回調(diào)函數(shù)
const observer = new MutationObserver(this.callback)
observer.disconnect()
// 以上述配置開始觀察目標(biāo)節(jié)點(diǎn)
observer.observe(targetNode, config)
},
// 當(dāng)觀察到變動時執(zhí)行的回調(diào)函數(shù)
callback (mutationsList, observer) {
const watermark = this.watermakr
const style = this.style
const currStyle = watermark.getAttribute('style')
for (let mutation of mutationsList) {
// 檢測元素樣式被修改
if (
mutation.type === 'attributes' &&
mutation.target === watermark &&
currStyle !== style
) {
watermark.setAttribute('style', style)
}
// 檢測元素被刪除
mutation.removedNodes.forEach(item => {
console.log(item.type, item.target, currStyle)
if (item === watermark) {
// document.body.appendChild(targetNode)
this.canvasWaterMark(180, 100, '水印加密')
this.createObserver()
}
})
}
}
6. 圖片加水印
在圖片上加水印的實(shí)現(xiàn)思路是,圖片加載成功后畫到canvas中,隨后在canvas中繪制水印,完成后通過canvas.toDataUrl()方法獲得base64并替換原來的圖片路徑
addWaterMarkToImg (imgUrl, cb, content) {
const img = new Image()
img.src = imgUrl
img.crossOrigin = 'anonymous'
img.onload = function () {
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.font = '20px Microsoft Yahei'
ctx.fillStyle = 'rgba(184, 184, 184, 0.8)'
const textX = 100
const textY = 30
ctx.fillText(content, img.width - textX, img.height - textY)
const base64Url = canvas.toDataURL()
cb && cb(base64Url)
}
// 給圖片加水印
const url = 'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/66a8e7cc0a0d4b0ba4130a43c796ad52~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image'
this.addWaterMarkToImg(url, (base64Url) => {
document.querySelector('img').src = base64Url
}, '水印加密')
7. 暗水印
暗水印是一種肉眼不可見的水印方式,可以保持圖片美觀的同時,保護(hù)資源版權(quán)
原理 :暗水印的生成方式有很多,常見的為通過修改RGB分量值的小量變動、DWT、DCT 和 FFT 等等方法。每個像素點(diǎn)都是由 RGB 三種元素構(gòu)成。當(dāng)我們把其中的一個分量修改,人的肉眼是很難看出其中的變化。
簡單的說,對圖片像素的處理,加密的信息散布在每個像素點(diǎn)上。
到此這篇關(guān)于前端水印實(shí)現(xiàn)方案的文章就介紹到這了,更多相關(guān)前端水印實(shí)現(xiàn)方案內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
ElementUI在實(shí)際項(xiàng)目使用步驟詳解
這篇文章主要介紹了ElementUI在實(shí)際項(xiàng)目使用的功能總結(jié),本文分步驟通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-07-07
JS簡單實(shí)現(xiàn)點(diǎn)擊按鈕或文字顯示遮罩層的方法
這篇文章主要介紹了JS簡單實(shí)現(xiàn)點(diǎn)擊按鈕或文字顯示遮罩層的方法,涉及javascript鼠標(biāo)事件響應(yīng)及頁面元素屬性動態(tài)操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-04-04
JS實(shí)戰(zhàn)之寫一個登錄表單驗(yàn)證學(xué)會判斷輸入合法性
JS表單驗(yàn)證是Web前端開發(fā)中一項(xiàng)基礎(chǔ)而關(guān)鍵的技術(shù)能力,其核心目標(biāo)是在用戶提交數(shù)據(jù)前,通過客戶端腳本對輸入內(nèi)容進(jìn)行即時、高效、友好的合法性校驗(yàn),這篇文章主要介紹了JS實(shí)戰(zhàn)之寫一個登錄表單驗(yàn)證學(xué)會判斷輸入合法性的相關(guān)資料,需要的朋友可以參考下2026-03-03
Node的優(yōu)勢我就不再亂吹捧了,它讓javascript統(tǒng)一web的前后臺成為了可能。但是對于新手來說,server端的JS代碼可能不像client端的代碼那么好調(diào)試,直觀。client端JS代碼的調(diào)試基本上經(jīng)歷了一個從“肉眼--alert()--firebug(或者其它的developer tools)”的一個過程。而對于server端的調(diào)試,可能新手仍然停留在使用“肉眼--console()”的階段。其實(shí),Node經(jīng)過了這么多年(雖然才短短幾年)的發(fā)展,也有了很多不錯的第三方的調(diào)試工具。包括Node內(nèi)建的調(diào)試工具debugger、node-inspector等。2014-05-05

