在网页设计中,为了保护内容的版权以及增加一些特殊效果,经常需要在页面上添加水印。本文将介绍一种通过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>