在网页设计中,为了保护内容的版权以及增加一些特殊效果,经常需要在页面上添加水印。本文将介绍一种通过Canvas和JavaScript实现在网页上添加水印的方法。
功能:
- 允许自定义水印内容、字体颜色
- 可以防止用户删除水印元素、修改样式等其他手段隐藏水印
效果
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
* {
margin: 0;
padding: 0;
}
.watermarked {
width: 1109px !important;
height: 4042px !important;
max-width: unset !important;
max-height: unset !important;
position: absolute !important;
top: 0 !important;
left: 0 !important;
padding: 0 !important;
margin: 0 !important;
z-index: 11 !important;
pointer-events: none !important;
background-repeat: repeat !important;
opacity: 0.5 !important;
display: block !important;
visibility: visible !important;
clip: initial !important;
clip-path: initial !important;
transform: initial !important;
}
</style>
</head>
<body>
<div class="div1">
<div id="watermark-container" class="watermarked"></div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
ensureWatermarkExists();
observeDocument();
});
function ensureWatermarkExists() {
let container = document.getElementById('watermark-container');
if (!container) {
container = document.createElement('div');
container.id = 'watermark-container';
document.body.appendChild(container);
}
resetStyles(container);
}
function resetStyles(element) {
// 定义原始样式
const originalStyles = {
width: '1109px',
height: '4042px',
position: 'absolute',
top: '0',
left: '0',
padding: '0',
margin: '0',
zIndex: '11',
pointerEvents: 'none',
backgroundRepeat: 'repeat',
opacity: '0.5',
display: 'block',
visibility: 'visible',
clip: 'initial',
clipPath: 'initial',
transform: 'initial'
};
// 应用原始样式
for (const [key, value] of Object.entries(originalStyles)) {
element.style[key] = value;
}
createWatermark();
}
let originalParentNode = null; // 用于保存原始的父节点
let originalNextSibling = null; // 用于保存原始的相对位置
function ensureWatermarkExists() {
let container = document.getElementById('watermark-container');
if (!container) {
container = document.createElement('div');
container.id = 'watermark-container';
// 在正确的位置插入
if (originalParentNode) {
originalParentNode.insertBefore(container, originalNextSibling);
} else {
// 如果没有原始位置信息,直接添加到文档的末尾
document.body.appendChild(container);
}
}
resetStyles(container);
}
function observeDocument() {
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList') {
let watermarkRemoved = Array.from(mutation.removedNodes).some(node => node.id === 'watermark-container');
if (watermarkRemoved) {
// 记住原始位置
originalParentNode = mutation.target;
originalNextSibling = findNextSibling(mutation);
console.log('watermark-container 节点被删除,尝试重新创建...');
ensureWatermarkExists();
}
} else if (mutation.type === 'attributes' && mutation.target.id === 'watermark-container') {
console.log('watermark-container 样式被修改,尝试重置...');
resetStyles(mutation.target);
}
});
});
// 配置观察器选项:
const config = { childList: true, subtree: true, attributes: true };
// 开始观察整个文档
observer.observe(document, config);
}
// 找到下一个兄弟节点
function findNextSibling(mutation) {
const siblings = Array.from(mutation.target.childNodes);
const indexOfRemovedNode = siblings.findIndex(node => node.id === 'watermark-container');
return siblings[indexOfRemovedNode + 1];
}
function createWatermark() {
const watermarkText = 'zhangsan(张三)1234332'; // 水印文本
const fontSize = 20; // 字体大小
const canvas = document.createElement('canvas');
fillText(canvas, watermarkText, fontSize);
// 将画布作为背景图像应用到容器中
const container = document.getElementById('watermark-container');
container.style.backgroundImage = `url(${canvas.toDataURL()})`;
}
function fillText(canvas, txt, fontSize) {
const ctx = canvas.getContext('2d');
const ratio = window.devicePixelRatio || 1;
let width = 300;
let height = 300;
canvas.width = width;
canvas.height = height;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
// 计算新字体大小
ctx.fillStyle = 'red';
ctx.font = fontSize + 'px serif';
let textWidth = ctx.measureText(txt).width;
let availableWidth = Math.sqrt((width * width) / 2); // 考虑到旋转后的可用宽度
let newFontSize = Math.min(fontSize, availableWidth / textWidth * fontSize) * ratio;
ctx.font = newFontSize + 'px serif';
textWidth = ctx.measureText(txt).width; // 重新计算调整后的文本宽度
// 旋转文本
ctx.translate(width / 2, height / 2); // 将旋转中心移至画布中心
ctx.rotate(-45 * Math.PI / 180); // 旋转 -45 度
// 重新定位并填充文本
ctx.fillText(txt, -textWidth / 2, newFontSize / 4);
}
</script>
</body>
</html>