最近在刷到一篇關於水印的文章,做者分享的幾種實現方式,結合到本身的項目也有用到,因此將主要的前端生成策略進行了封裝,以便往後項目使用。css
canvas 有着不錯的兼容性,是一種比較可靠、成熟的可視化技術。可是它比較依賴分辨率,對文本的處理上也有着先天的不足。可是它能夠很方便的將結果保存爲圖片,對於完成水印的需求也是很是合適的。git
爲了方便使用者上手,我將全部的實現座標都設置爲top/left,以方便對x、y的設置。github
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 與 canvas 相比瀏覽器兼容性幾乎一致,除了幾個早期的 Android 版本,這樣的設備以及很難找到了,徹底能夠忽略。svg 使用的是 XML 的方式,不依賴分辨率,在作文本水印這件事上 svg 有着更好的優點。npm
svg 的 text 屬性 x、y,是將文本左下位置定位到其座標系的(x,y)位置,這可能和平常寫 css 的定位不一樣,全部須要有一個 dy 值,設置其偏移量。canvas
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)))}`;
}
}
複製代碼
使用元素生成是一種很傳統的方式,在本次實踐中,我並無考慮兼容性,由於使用了 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是變更觀察器,字面上就能夠理解這是用來觀察Node(節點)變化的。MutationObserver是在DOM4規範中定義的,它的前身是MutationEvent事件,最低支持版本爲 ie9 ,目前已經被棄用。bash
在這裏咱們主要觀察的有三點app
爲了更少的觸發觀察者,我寫了兩個配置,第一個針對水印元素自己 {characterData: true, attributes: true, childList: true, subtree: true}
對全部的異動處理,第二個觀察者主要對 body 下的 {childList: true}
敏感,也就是子元素的改變,在 body 觀察者的回調只對有 removedNodes 這件事作出反應。svg
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
})
複製代碼