前端頁面水印生成實現

前端頁面水印生成實現的圖1

Github?|?  NPM?|? DEMO

canvas 生成方式

canvas 有著不錯的兼容性,是一種比較可靠、成熟的可視化技術。但是它比較依賴分辨率,對文本的處理上也有著先天的不足。但是它可以很方便的將結果保存為圖片,對于完成水印的需求也是非常合適的。

前端頁面水印生成實現的圖2

為了方便使用者上手,我將所有的實現坐標都設置為top/left,以方便對x、y的設置。

export default class CanvasWay {     constructor(watermark) {         this.watermark = watermark         const {width, height} = watermark         this.canvas = document.createElement('canvas');         this.canvas.setAttribute('width', width);         this.canvas.setAttribute('height', height);     }     render() {         const {txt, x, y, width, height, font, color, fontSize, alpha, angle} = this.watermark         const ctx = this.canvas.getContext('2d');         ctx.clearRect(0, 0, width, height);         ctx.textBaseline = 'top';         ctx.textAlign = 'left'         ctx.fillStyle = color;         ctx.globalAlpha = alpha;         ctx.font = `${fontSize}px ${font}`         ctx.translate(x, y)         ctx.rotate(Math.PI / 180 * angle);         ctx.translate(-x, -y - fontSize)         ctx.fillText(txt, x, y + fontSize);         return this.canvas.toDataURL();     } } 復制代碼

svg 生成方式

svg 與 canvas 相比瀏覽器兼容性幾乎一致,除了幾個早期的 Android 版本,這樣的設備以及很難找到了,完全可以忽略。svg 使用的是 XML 的方式,不依賴分辨率,在做文本水印這件事上 svg 有著更好的優勢。

前端頁面水印生成實現的圖3

svg 的 text 屬性 x、y,是將文本左下位置定位到其坐標系的(x,y)位置,這可能和日常寫 css 的定位不同,所有需要有一個 dy 值,設置其偏移量。

export default class SvgWay {     constructor(watermark) {         this.watermark = watermark     }     render() {         const {txt, x, y, width, height, color, font, fontSize, alpha, angle} = this.watermark         const svgStr =             `<svg xmlns="http://www.w3.org/2000/svg" width="${width}px" height="${height}px">                 <text x="${x}px" y="${y}px" dy="${fontSize}px"                     text-anchor="start"                     stroke="${color}"                     stroke-opacity="${alpha}"                     fill="none"                     transform="rotate(${angle},${x} ${y})"                     font-weight="100"                     font-size="${fontSize}"                     font-family="${font}"                     >                     ${txt}                 </text>             </svg>`;         return `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svgStr)))}`;     } } 復制代碼

element 生成方式

使用元素生成是一種很傳統的方式,在本次實踐中,我并沒有考慮兼容性,因為使用了 CSS3 的屬性 transform 所以在ie9 以下也是不能勝任的,但是因為有這種方式的實現,我們可以根據需求在后續補充不同的瀏覽器 Hack,如: filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); 使用這些代碼,就可以完整覆蓋到更多的瀏覽器。

import bindCSS from '../helpers/bindCSS' export default class ElementWay {     constructor(watermark) {         this.watermark = watermark     }     _createItem() {         let {txt, x, y, font, color, fontSize, alpha, angle, width, height} = this.watermark         const item = document.createElement('div');         bindCSS(item, {             position: 'relative',             width, height,             flex: `0 0 ${width}px`,             overflow: 'hidden',             pointerEvents: 'none'         })         let span = document.createElement('span');         span.innerHTML = txt         bindCSS(span, {             position: 'absolute',             top: `${y}px`,             left: `${x}px`,             fontFamily: font,             fontSize: `${fontSize}px`,             color: color,             lineHeight: 1.5,             opacity: alpha,             fontWeight: 400,             transform: `rotate(${angle}deg)`,             transformOrigin: '0 0',             userSelect: 'none',             whiteSpace: 'nowrap',             overflow: 'hidden'         })         item.appendChild(span)         return item;     }     render() {         const {width, height} = this.watermark         const {clientWidth, clientHeight} = document.documentElement || document.body         const column = Math.ceil(clientWidth / width)         const rows = Math.ceil(clientHeight / height)         const wrap = document.createElement('div');         bindCSS(wrap, {             display: 'flex',             flexWrap: 'wrap',             width: `${width * column}px`,             height: `${height * rows}px`         })         for (let i = 0; i < column * rows; i++) {             wrap.appendChild(this._createItem());         }         return wrap;     } } 復制代碼

MutationObserver?元素異動監控

MutationObserver 對現代瀏覽的兼容性還是不錯的,MutationObserver是變動觀察器,字面上就可以理解這是用來觀察Node(節點)變化的。MutationObserver是在DOM4規范中定義的,它的前身是MutationEvent事件,最低支持版本為 ie9 ,目前已經被棄用。

前端頁面水印生成實現的圖4

在這里我們主要觀察的有三點

  • 水印元素本身是否被移除

  • 水印元素屬性是否被篡改(display:none ...)

  • 水印元素的子元素是否被移除和篡改 (主要考慮 element的實現方式 )

為了更少的觸發觀察者,我寫了兩個配置,第一個針對水印元素本身 {characterData: true, attributes: true, childList: true, subtree: true} 對所有的異動處理,第二個觀察者主要對 body 下的 {childList: true} 敏感,也就是子元素的改變,在 body 觀察者的回調只對有 removedNodes 這件事做出反應。

const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; export const observer = (callback) => {     if (!MutationObserver) return false     let bodyObserver = new MutationObserver(         mutationsList => mutationsList.forEach(mutation =>             mutation.removedNodes.forEach(                 _target => _target.id === _id && callback()             )         )     )     bodyObserver.observe(document.body, {childList: true});     const target = document.getElementById(_id);     let observer = new MutationObserver(callback);     observer.observe(target, {characterData: true, attributes: true, childList: true, subtree: true});     return {bodyObserver, observer}; } 復制代碼

最終工具調用方式

    gwm.creation({         mode: 'svg',         watch: false,         fontSize: 13,         color: '#000',         font: 'sans-serif',         alpha: 0.2,         angle: -15     }) 復制代碼


作者:LoadChange
來源:掘金

登錄后免費查看全文
立即登錄
App下載
技術鄰APP
工程師必備
  • 項目客服
  • 培訓客服
  • 平臺客服

TOP