通过 JavaScript,我们可以创建一个网页水印功能,并利用 MutationObserver API 监控 DOM 变化,防止水印被删除。以下是一个完整的实现示例:
功能特点:
- 动态添加水印。
- 使用
MutationObserver监控水印是否被删除或修改,若有变化,则立即恢复。
1. DOM实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Page Watermark</title>
<style>
#watermark {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none; /* 确保水印不影响页面交互 */
z-index: 9999;
}
.watermark-text {
position: absolute;
color: rgba(0, 0, 0, 0.1); /* 半透明颜色 */
font-size: 20px;
transform: rotate(-30deg);
user-select: none; /* 禁止选中 */
white-space: nowrap;
}
</style>
</head>
<body>
<h1>网页水印示例</h1>
<p>这是一个展示带有防清除功能水印的网页。</p>
<script>
// 创建水印函数
function createWatermark(text) {
// 如果水印已存在,先删除
const existingWatermark = document.getElementById('watermark');
if (existingWatermark) {
existingWatermark.remove();
}
// 创建水印容器
const watermarkContainer = document.createElement('div');
watermarkContainer.id = 'watermark';
// 文档碎片
const fragment = document.createDocumentFragment()
// 计算行列数
const cols = Math.ceil(window.innerWidth / 200);
const rows = Math.ceil(window.innerHeight / 100);
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
const watermarkText = document.createElement('div');
watermarkText.className = 'watermark-text';
watermarkText.innerText = text;
watermarkText.style.top = `${i * 100}px`;
watermarkText.style.left = `${j * 200}px`;
fragment.appendChild(watermarkText)
}
}
watermarkContainer.appendChild(fragment);
document.body.appendChild(watermarkContainer);
}
// 初始化水印
createWatermark('Confidential');
// 使用 MutationObserver 监听水印是否被删除
const observer = new MutationObserver(() => {
const watermark = document.getElementById('watermark');
if (!watermark) {
createWatermark('Confidential');
}
});
// 监听整个 body
observer.observe(document.body, {
childList: true, // 监控子节点的变化
subtree: true, // 监控所有子树
});
// 监听窗口大小变化,动态调整水印
window.addEventListener('resize', () => {
createWatermark('Confidential');
});
</script>
</body>
</html>
2. canvas实现
<style>
body {
font-family: Arial, sans-serif;
}
#watermarkCanvas {
position: absolute;
top: 0;
left: 0;
pointer-events: none; /* 确保水印不影响页面交互 */
z-index: 9999;
}
</style>
<script>
let watermarkCanvas = null;
let watermarkCtx = null;
// 创建 Canvas 水印函数
function createWatermark(text) {
if (!watermarkCanvas) {
watermarkCanvas = document.createElement('canvas');
watermarkCanvas.id = 'watermarkCanvas';
document.body.appendChild(watermarkCanvas);
}
const canvasWidth = window.innerWidth; // 计算 Canvas 宽度
const canvasHeight = window.innerHeight; // 计算 Canvas 高度
if (watermarkCanvas.width !== canvasWidth || watermarkCanvas.height !== canvasHeight) {
watermarkCanvas.width = canvasWidth;
watermarkCanvas.height = canvasHeight;
// 设置 canvas 绘制的真实大小
watermarkCanvas.width = window.innerWidth
watermarkCanvas.height = window.innerHeight
watermarkCtx = watermarkCanvas.getContext('2d');
}
watermarkCtx.clearRect(0, 0, watermarkCanvas.width, watermarkCanvas.height); // 清除之前的内容
watermarkCtx.globalAlpha = 0.1; // 使用 globalAlpha 确保透明度设置的一致性
watermarkCtx.fillStyle = 'rgba(0, 0, 0, 1)'; // 设置填充颜色,透明度通过 globalAlpha 控制
watermarkCtx.font = '24px sans-serif'; // 设置字体大小
watermarkCtx.textAlign = 'center';
watermarkCtx.textBaseline = 'middle';
const rowSpacing = 120; // 行间距
const colSpacing = 100; // 列间距
const rows = Math.ceil(watermarkCanvas.height / rowSpacing); // 行数
const cols = Math.ceil(watermarkCanvas.width / colSpacing); // 列数
// 绘制水印
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
watermarkCtx.save(); // 保存当前状态
watermarkCtx.translate(j * colSpacing + 100, i * rowSpacing + 50); // 平移到正确的位置
watermarkCtx.rotate(-30 * Math.PI / 180); // 旋转 -30 度
watermarkCtx.fillText(text, 0, 0); // 绘制水印文本
watermarkCtx.restore(); // 恢复之前的状态
}
}
}
// 初始化水印
createWatermark('机密文件');
// 使用 MutationObserver 监听水印是否被删除
const observer = new MutationObserver(() => {
if (!document.getElementById('watermarkCanvas')) {
createWatermark('机密文件');
}
});
// 监听整个 body
observer.observe(document.body, {
childList: true, // 监控子节点的变化
subtree: true, // 监控所有子树
});
// 监听窗口大小变化,动态调整水印
window.addEventListener('resize', () => {
createWatermark('机密文件');
});
</script>
代码说明:
-
水印绘制:
- 水印通过
div元素生成,pointer-events: none确保不会影响页面操作。 - 每行间隔和列间隔可根据窗口大小动态计算。
- 使用
rgba半透明颜色和旋转效果,确保水印清晰可见但不干扰内容。
- 水印通过
-
防清除功能:
- 使用
MutationObserver监听body元素的子节点变化。 - 如果水印被删除,自动重新添加水印。
- 使用
-
窗口大小调整:
- 监听
resize事件,确保在调整窗口时,水印布局自动更新。
- 监听
测试方法:
- 打开页面后,尝试手动从 DOM 中删除水印(在控制台执行
document.getElementById('watermark').remove())。 - 观察水印是否会重新出现。
- 调整窗口大小,水印应自动调整布局。
注意事项:
- 无法完全防止清除:如果恶意用户禁用了 JavaScript,或者通过高级方式清空 DOM,再恢复功能会受到限制。
- 提高复杂性:可以混淆或动态生成水印代码,增加清除难度。