最近在公司项目里接触了前端水印相关的需求,在这里分享一下如何把水印封装成组件吧~
分类
我了解到的水印实现方式有三种:div遮罩、svg背景图、canvas背景图,我这里简单讲下后两种。
svg
话不多说,直接上代码
// 这里是因为原生 btoa 不能转义中文字符
function Btoa(text: string) {
return btoa(unescape(encodeURIComponent(text)));
}
function generateSVGBase64(props) {
const { width, height, color, rotate, fontSize, fontFamily } = props;
let { text } = this.props;
if (Array.isArray(text)) {
if (text.length === 1) {
text = text[0];
} else {
text = text
.map((item, index) => `<tspan x="0" y="${(index + 1) * fontSize}">${item}</tspan>`)
.join('');
}
}
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%">
<style type="text/css">
text {
fill: ${color};
stroke: ${color};
font-size: ${fontSize}px;
font-family: ${fontFamily};
}
</style>
<defs>
<pattern id="combo" width="${width}" height="${height}"
patternTransform="rotate(${rotate})" patternUnits="userSpaceOnUse">
<text>${text}</text>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#combo)"/>
</svg>`;
return `url('data:image/svg+xml;base64,${Btoa(svg.trim())}')`;
}
export default function Watermark(props) {
let { children, className, style } = props;
const backgroundImage = generateSVGBase64(props);
style = {
backgroundImage,
...style,
};
return (
<div className={className} style={style}>
{children}
</div>
);
}
这里先通过 Btoa 方法将 svg 字符串转成 base64 的url,然后通过 backgroundImage 属性设置成元素的背景图即可。
canvas
这里只放上生成 url 的方法
function generateCanvasUrl(props) {
const { gapX, gapY, text, width, height, rotate, fontSize, fontFamily, fontWeight,fontStyle } = props;
const canvas = document.createElement('canvas');
const ratio = window.devicePixelRatio;
const ctx = canvas.getContext('2d');
const canvasWidth = `${(gapX + width) * ratio}px`;
const canvasHeight = `${(gapY + height) * ratio}px`;
const markWidth = width * ratio;
const markHeight = height * ratio;
canvas.setAttribute('width', canvasWidth);
canvas.setAttribute('height', canvasHeight);
if (ctx) {
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
// 文字绕中间旋转
ctx.translate(markWidth / 2, markHeight / 2);
ctx.rotate((Math.PI / 180) * Number(rotate));
const markSize = Number(fontSize) * ratio;
ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`;
ctx.fillStyle = color;
if (Array.isArray(text)) {
text.forEach((item: string, index: number) => ctx.fillText(item, 0, index * markSize));
} else {
ctx.fillText(text, 0, 0);
}
ctx.restore();
return `url('${canvas.toDataURL()}')`;
}
}
得到生成后的 url 之后作为 backgroundImage 设置在元素上即可~