使用JS代碼實(shí)現(xiàn)頁面添加水印的方法詳解
一、水印概括
1. 添加水印的好處
- 信息標(biāo)識: 水印可以用于標(biāo)識文檔的所有者、保密級別、狀態(tài)或其他相關(guān)信息,幫助用戶更好地理解文檔內(nèi)容的屬性。
- 版權(quán)保護(hù): 在文檔中添加水印可以幫助保護(hù)內(nèi)容的版權(quán),防止他人未經(jīng)授權(quán)地復(fù)制、轉(zhuǎn)載或篡改內(nèi)容。
- 安全保護(hù): 對于敏感信息或機(jī)密文檔,添加水印可以幫助防止信息泄露,提高文檔的安全性。
- 提升專業(yè)性: 在一些場景下,如商業(yè)報(bào)告、合同文件等,添加水印可以增加文檔的專業(yè)性和正式性。
- 防止截屏或拷貝: 在網(wǎng)頁中添加水印可以防止用戶通過截屏或復(fù)制粘貼等方式非法獲取文檔內(nèi)容。
2. 添加水印的壞處
- 視覺干擾: 過多或過顯眼的水印可能會(huì)影響頁面的美觀度和可讀性,甚至干擾用戶對頁面內(nèi)容的瀏覽和理解。
- 性能影響: 在頁面中添加水印可能會(huì)增加頁面加載時(shí)間和資源消耗,特別是對于大型或復(fù)雜的水印圖案或動(dòng)態(tài)水印。
- 用戶體驗(yàn): 一些用戶可能會(huì)認(rèn)為水印對頁面內(nèi)容的查看和操作產(chǎn)生不必要的干擾,從而降低他們的使用體驗(yàn)。
- 不適用于移動(dòng)端: 在移動(dòng)設(shè)備上,屏幕空間有限,添加水印可能會(huì)占用寶貴的屏幕空間,影響用戶的閱讀和操作。
二、技術(shù)方案
1. watermark 第三方庫
優(yōu)勢:已有 github 庫,可直接使用 劣勢:
- 庫比較老,多年未更新
- 只支持圖片水印,不支持文案水印
- 不支持多行水印
- 包比較重,增加
SDK負(fù)擔(dān)
2. JS 簡單實(shí)現(xiàn)水印功能
優(yōu)勢:
- 可支持圖片和文案水印
- 封裝函數(shù)即可,輕量 劣勢:需要自己封裝
三、水印功能實(shí)現(xiàn)
1. 水印功能需求
- 需要支持文案和圖片
- 可設(shè)置文案顏色字體等樣式
- 可支持多行文字水印
- 可動(dòng)態(tài)控制水印間隔
- 能鋪滿全屏
- 可旋轉(zhuǎn)
- 不影響頁面其他模塊背景展示
2. 功能實(shí)現(xiàn)
/**
* 水印功能代碼實(shí)現(xiàn)
*/
interface IWarterMarkProps{
text?: string | string[] // 水印文案
imgUrl?: string // 水印圖片
imageOpacity?: number // 圖片透明度
fontStyle?: string // 字體樣式
fontVariant?: string // 字體變體
fontWeight?: number // 字體粗細(xì)
fontFamily?: string // 字體
fontSize?: number // 字體大小
lineHeight?: number // 字體行高
fontColor?: string // 字體顏色
rotate?: number // 水印旋轉(zhuǎn)角度 deg
canvasRotate?: number // canvas 旋轉(zhuǎn)角度 deg
imageWidth?: number // 圖片寬度
imageHeight?: number // 圖片高度
xOffset?: number // 水印 x 偏移
yOffset?: number // 水印 y 偏移
width?: number // 寬度
height?: number // 高度
zIndex: number // 層級
}
interface ISetDomProps{
url: string // 圖片 URL
rotate?: number // 水印旋轉(zhuǎn)角度
zIndex?: number // 層級
width?: number // 寬度
}
/**
* 設(shè)置 dom CSS 屬性
* @param {ISetDomProps} props dom 數(shù)據(jù)類型
*/
const setDomCSSAttr = (props: ISetDomProps) => {
const {
url, rotate = 0, zIndex = 10, width = 32
} = props
let waterMarker = document.getElementById('water-marker')
let isInitDom = true
if (!waterMarker) {
isInitDom = false
waterMarker = document.createElement('div')
waterMarker.id = 'water-marker'
}
Object.assign(waterMarker.style, {
transform: rotate ? `rotate(${rotate}deg)` : '',
backgroundSize: `${width}px`,
backgroundImage: `url(${url}), url(${url})`,
backgroundRepeat: 'repeat',
position: 'fixed',
left: 0,
top: 0,
zIndex,
width: '100vw',
height: '100vh',
pointerEvents: 'none',
mixBlendMode: 'multiply'
});
!isInitDom && document.body.appendChild(waterMarker)
}
/**
* 獲取 canvas 像素比
* @param {CanvasRenderingContext2D} context
* @returns 像素比
*/
const getRatio = (context: any) => {
if (!context) {
return 1;
}
const backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1
return (window.devicePixelRatio || 1) / backingStore
}
/**
* 設(shè)置水印
* @param {IWarterMarkProps} props 水印各種屬性
*/
const setWarterMarker = (props: IWarterMarkProps) => {
const {
imgUrl = '', imageOpacity = 1, imageWidth = 0, imageHeight = 0, fontColor = 'rgba(128, 128, 128, .2)',
fontVariant = 'normal', fontWeight = 400, fontSize = 14, lineHeight = 14, fontStyle = 'normal', fontFamily = 'arial', rotate = 0, canvasRotate = 0,
yOffset = 0, xOffset = 0, width = 200, height = 200, zIndex = 10,
} = props
const text = typeof props.text === 'object' ? props.text : props.text ? [props.text] : []
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
const ratio = getRatio(ctx);
const canvasWidth = width * ratio
const canvasHeight = height * ratio
const canvasOffsetLeft = xOffset * ratio
const canvasOffsetTop = yOffset * ratio
canvas.width = canvasWidth
canvas.height = canvasHeight
ctx.rotate(canvasRotate * (Math.PI / 180));
let url = ''
if (text.length) {
ctx.font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize * ratio}px/${lineHeight * ratio}px ${fontFamily || getComputedStyle(document.body).fontFamily}`
ctx.fillStyle = fontColor
text.forEach((content, index) => {
ctx.fillText(content, canvasOffsetLeft, index * lineHeight + canvasOffsetTop + lineHeight * ratio)
})
url = canvas.toDataURL()
}
if (imgUrl) {
const img = new Image();
img.crossOrigin = 'anonymous';
img.referrerPolicy = 'no-referrer';
img.src = imgUrl;
img.onload = () => {
ctx.globalAlpha = imageOpacity
ctx.drawImage(img, canvasOffsetLeft, canvasOffsetTop, (imageWidth || (imageHeight ? img.width * imageHeight / img.height : img.width)) * ratio, (imageHeight || (imageWidth ? img.height * imageWidth / img.width : img.height)) * ratio)
setDomCSSAttr({
url: canvas.toDataURL(),
rotate,
zIndex,
width
})
return
};
return
}
setDomCSSAttr({
url,
rotate,
zIndex,
width
})
}
3. 步驟解析
3.1. setWarterMarker 方法
setWarterMarker 是使用水印的方法,參數(shù)是一個(gè)對象,包含以下內(nèi)容
interface IWarterMarkProps{
text?: string | string[] // 水印文案
imgUrl?: string // 水印圖片
imageOpacity?: number // 圖片透明度
fontStyle?: string // 字體樣式
fontVariant?: string // 字體變體
fontWeight?: number // 字體粗細(xì)
fontFamily?: string // 字體
fontSize?: number // 字體大小
lineHeight?: number // 字體行高
fontColor?: string // 字體顏色
rotate?: number // 水印旋轉(zhuǎn)角度 deg
canvasRotate?: number // canvas 旋轉(zhuǎn)角度 deg
imageWidth?: number // 圖片寬度
imageHeight?: number // 圖片高度
xOffset?: number // 水印 x 偏移
yOffset?: number // 水印 y 偏移
width?: number // 寬度
height?: number // 高度
zIndex: number // 層級
}
- 獲取各個(gè)參數(shù)值
- 創(chuàng)建
canvas,獲取canvas的像素比,設(shè)置canvas的各種參數(shù) - 如果存在
text屬性,則進(jìn)行遍歷繪制 - 如果有圖片,則創(chuàng)建
image,再進(jìn)行繪制 - 生成
url,通過setDomCSSAttr方法進(jìn)行 dom 渲染
3.2. getRatio 方法
獲取 canvas 的像素比,更清晰的繪制 canvas
3.3. setDomCSSAttr 方法
接收一個(gè)對象,包含以下內(nèi)容
interface ISetDomProps{
url: string // 圖片 URL
rotate?: number // 水印旋轉(zhuǎn)角度
zIndex?: number // 層級
width?: number // 寬度
}
- 查找/創(chuàng)建
dom元素 - 設(shè)置
style屬性transform: 旋轉(zhuǎn)backgroundSize:背景大小backgroundImage:背景圖片backgroundRepeat:背景重復(fù)position:定位zIndex:層級pointerEvents:不能被選中mixBlendMode:控制元素的混合模式
3.4. CSS mix-blend-mode 屬性
mix-blend-mode 是 CSS 中的一個(gè)屬性,用于控制元素的混合模式。它可以讓元素的背景色和內(nèi)容色以一種特定的方式混合在一起,從而產(chǎn)生各種視覺效果。 以下是一些常用的 mix-blend-mode 值:
- normal:默認(rèn)值,內(nèi)容色完全覆蓋背景色。
- multiply:將內(nèi)容色與背景色相乘。通常用于創(chuàng)建暗色效果。
- screen:將內(nèi)容色與背景色相除,并取反。通常用于創(chuàng)建亮色效果。
- overlay:結(jié)合了
multiply和screen模式,根據(jù)背景色的亮度來決定內(nèi)容色的混合模式。 - darken:內(nèi)容色和背景色中的每個(gè)像素值,取對應(yīng)像素值的較小者。
- lighten:內(nèi)容色和背景色中的每個(gè)像素值,取對應(yīng)像素值的較大者。
- color-dodge:顏色減淡,通過減少背景色的值來增加內(nèi)容色的亮度。
- color-burn:顏色加深,通過增加背景色的值來降低內(nèi)容色的亮度。
- difference:計(jì)算內(nèi)容色和背景色之間的差異,產(chǎn)生顏色反轉(zhuǎn)的效果。
- exclusion:將內(nèi)容色和背景色相減,并添加其結(jié)果的兩倍。
- hue:保留內(nèi)容色的色相,并使用背景色的飽和度和亮度。
- saturation:保留內(nèi)容色的飽和度,并使用背景色的色相和亮度。
- color:保留內(nèi)容色的色相和飽和度,并使用背景色的亮度。
- luminosity:保留內(nèi)容色的亮度,并使用背景色的色相和飽和度。
使用 mix-blend-mode 屬性,可以輕松地創(chuàng)建各種視覺效果,例如顏色混合、遮罩效果等。
三、水印使用
1. 文字
setWarterMarker({
text: '這是水印, 這是水印, 這是水印',
width: 200,
height: 200,
fontColor: 'red',
fontSize: 8,
lineHeight: 10,
})

2. 圖片
setWarterMarker({
imgUrl: 'https://sponsors.vuejs.org/images/chrome_frameworks_fund.png',
imageHeight: 50,
imageWidth: 50,
imageOpacity: 0.1,
})

3. 圖片和文字結(jié)合
setWarterMarker({
text: '這是水印, 這是水印, 這是水印',
width: 200,
height: 200,
fontColor: 'red',
fontSize: 8,
lineHeight: 10,
imgUrl: 'https://sponsors.vuejs.org/images/chrome_frameworks_fund.png',
imageHeight: 50,
imageWidth: 50,
imageOpacity: 0.1,
canvasRotate: 15
})

4. 多行水印和圖片
setWarterMarker({
text: ['這是水印, 這是水印, 這是水印', '第二行水印', '第三行水印', '第四行水印'],
width: 200,
height: 200,
fontColor: 'red',
fontSize: 8,
lineHeight: 40,
imgUrl: 'https://sponsors.vuejs.org/images/chrome_frameworks_fund.png',
imageHeight: 50,
imageWidth: 50,
imageOpacity: 0.1,
})

四、總結(jié)
- 水印技術(shù)調(diào)研顯示,水印在保護(hù)版權(quán)、信息標(biāo)識和安全保護(hù)方面具有重要作用。
- 通過CSS、JavaScript或第三方庫,可以實(shí)現(xiàn)頁面水印功能。
- 過多或顯眼的水印可能影響用戶體驗(yàn),而性能和兼容性也是考慮的因素。
- 綜合評估水印的利弊,可結(jié)合實(shí)際需求謹(jǐn)慎選擇適合的實(shí)現(xiàn)方式。
- 如果需求有更復(fù)雜的水印,可以按步驟去改即可。
以上就是使用JS代碼實(shí)現(xiàn)頁面添加水印的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于JS頁面添加水印的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js實(shí)現(xiàn)文字在按鈕上滾動(dòng)的方法
這篇文章主要介紹了js實(shí)現(xiàn)文字在按鈕上滾動(dòng)的方法,涉及javascript動(dòng)態(tài)定時(shí)操作頁面元素樣式的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08
走進(jìn)javascript——不起眼的基礎(chǔ),值和分號
本文主要介紹了javascript的基礎(chǔ)知識--值和分號,具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-02-02
JavaScript生成驗(yàn)證碼并實(shí)現(xiàn)驗(yàn)證功能
這篇文章主要介紹了JavaScript生成驗(yàn)證碼并實(shí)現(xiàn)驗(yàn)證功能的相關(guān)資料,代碼簡單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
js實(shí)現(xiàn)頂部可折疊的菜單工具欄效果實(shí)例

