一、 需求背景
为了防止内部数据泄露,拍照等等,预览文档,添加水印功能,可以添加登录人姓名,公司名称,公司logo等信息。
二、 实现方式
框架是用react实现水印相关功能,在页面内容区域添加一个盒子,根据页面的大小,水印间距参数计算水印的行数和列数。
特点:
1.支持水印反删除,监视 DOM 的变动,水印不可被删除、且属性不可被修改,监听DOMNodeRemoved,DOMSubtreeModified方法。
2. 动态水印设置:在浏览器地址后面拼接水印参数,根window.location.search获取水印信息,重新设置水印参数。
3. 移动端和pc端水印保持一致,实现思路,定义一个基准,根据页面的宽度和基准页面的宽度得到一个比值,去动态设置水印的字体,水印间距等相关信息。
4. 支持图片水印,换行水印,单个水印,平铺水印,动态设置水印,非常灵活。
具体详细可参考永中文档在线预览功能www.yozodcs.com/page/exampl…
三 、效果图如下
pc端效果图:
移动端效果图:
四、具体实现
水印参数
// 水印参数
export interface WaterMark {
watermark_txt: string;
watermark_x: number;
watermark_y: number;
watermark_x_extra: number;
watermark_y_extra: number;
watermark_fontSize: number;
watermark_tile: boolean;
watermark_angle: number;
watermark_alpha: number;
watermark_family: string;
watermark_fontColor: string;
watermark_picPath: string;
watermark_picWidth: number;
watermark_picHeight: number;
}
根据页面的大小,水印间距参数计算水印的行数和列数。
/**
* 水印处理相关
* @param wrapperW 水印展示包裹区宽度
* @param wrapperH 水印展示包裹区高度
* @param watermarkW 旋转(仿射)变换后水印的宽度
* @param watermarkH 旋转(仿射)变换后水印的高度
* @return pos 各水印定位位置组成的数组
*/
export function handleWatermark(
wrapperW: number,
wrapperH: number,
watermarkW: number,
watermarkH: number,
watermarkInfo: WaterMark,
) {
const { watermark_y, watermark_x, watermark_y_extra, watermark_x_extra } =
watermarkInfo;
// 计算每行/列需要排布的数目
const nRows = Math.ceil(wrapperH / (watermarkH + watermark_y_extra));
const nCols = Math.ceil(wrapperW / (watermarkW + watermark_x_extra));
// 构建排布的各水印top/left
let pos = [],
top: number,
left: number;
// console.log(nRows, nCols);
for (let i = 0; i < nRows; i++) {
top = Math.round(watermark_y + i * (watermarkH + watermark_y_extra));
for (let j = 0; j < nCols; j++) {
// 偶数行保证参差感
if (i % 2 === 1) {
left = watermark_x + 50 + j * (watermarkW + watermark_x_extra);
} else {
left = watermark_x + j * (watermarkW + watermark_x_extra);
}
pos.push({
top: top,
left: left,
});
}
}
return pos;
}
水印不可篡改具体实现:监听事件shouldRerender,DOMSubtreeModified,页面dom元素是否改变或者删除,重新加载水印组件
useEffect(() => {
const wmWrapperEl = contentRef?.current?.parentNode;
const contentEl = contentRef?.current;
function shouldRerender() {
eventBus.emit('shouldRerender');
}
//水印不可删除
wmWrapperEl?.addEventListener('DOMNodeRemoved', shouldRerender);
//水印不可修改
contentEl?.addEventListener('DOMSubtreeModified', shouldRerender);
return () => {
wmWrapperEl?.removeEventListener('DOMNodeRemoved', shouldRerender);
contentEl?.removeEventListener('DOMSubtreeModified', shouldRerender);
};
}, [contentRef]);
五、结尾
本文仅介绍了水印的简单实现,也可以利用canvas实现水印,canvas元素其实就是一个画布,我们可以很方便地绘制一些文字、线条、图形等,它也可以将一个img标签里渲染的图片画在画布上。