1150针CPU选购指南:E3-1231 V3核显平替方案
2026/7/23 5:45:45
在前端开发中,网页水印是一个非常常见的需求,主要用于环境标识(如测试环境、生产环境)、版权保护、数据安全等场景。一个合格的网页水印需要满足防删除、自适应窗口、高兼容性等特性,本文将基于 Canvas + DOM 监听的方式,实现一个健壮的网页水印工具,并结合 Vue 项目演示实际使用流程。
网页水印的核心实现逻辑分为以下几步:
document.documentElement),确保水印覆盖整个页面。我们将水印功能封装为一个独立的工具类watermark.js,便于在项目中复用:
/** * 网页水印工具类 * 功能:生成防删除、自适应、高兼容性的网页水印 * 作者:前端无涯https://blog.csdn.net/weixin_44439817?type=blog * 日期:2025-12-15 */letwatermark={}/** * 生成水印元素并挂载到页面 * @param {string} str - 水印显示的文字内容 * @returns {string|null} - 水印元素的ID(失败返回null) */letsetWatermark=(str)=>{// 生成随机ID,防止被反调试脚本通过固定ID清除letid='wm-'+Math.random().toString(36).substr(2,9)try{// 1. 创建Canvas元素,绘制水印文字letcan=document.createElement('canvas')// 设置Canvas尺寸(水印的单个重复单元大小)can.width=200can.height=150letcans=can.getContext('2d')// 文字旋转(-20度,斜向显示)cans.rotate(-20*Math.PI/180)// 文字样式:字体、透明度、颜色cans.font='20px Verdana'cans.fillStyle='rgba(100, 100, 100, 0.2)'// 文字对齐方式cans.textAlign='left'cans.textBaseline='middle'// 绘制文字(位置:Canvas宽/8,高/2)cans.fillText(str,can.width/8,can.height/2)// 2. 创建水印容器divletdiv=document.createElement('div')div.id=id// 添加自定义属性,用于双重检测水印元素div.setAttribute('data-watermark','true')// 3. 设置水印容器样式(全屏覆盖、层级最高、不可点击)div.style.pointerEvents='none'// 防止水印遮挡页面元素的点击事件div.style.top='0px'div.style.left='0px'div.style.position='fixed'// 固定定位,跟随窗口滚动div.style.zIndex='2147483647'// 最高层级,确保覆盖所有元素div.style.width='100vw'// 视口宽度div.style.height='100vh'// 视口高度div.style.display='block !important'// 强制显示div.style.visibility='visible !important'// 强制可见// 4. 将Canvas图片设置为背景图(兼容CSP限制,添加try-catch)try{div.style.background='url('+can.toDataURL('image/png')+') left top repeat'}catch(e){console.error('Watermark canvas toDataURL error:',e)}// 5. 挂载到documentElement(比body更稳定,避免body被样式限制)document.documentElement.appendChild(div)console.log('Watermark element appended:',id)returnid}catch(err){console.error('Watermark creation failed:',err)returnnull}}/** * 初始化水印(对外暴露的方法,仅允许有效调用一次) * @param {string} str - 水印文字内容 */watermark.set=(str)=>{// 延迟执行(1秒),确保DOM完全加载后再生成水印setTimeout(()=>{letcurrentId=setWatermark(str)// 定时检测(每2秒),防止水印被删除或隐藏setInterval(()=>{// 检测方式1:通过ID判断水印元素是否存在if(currentId&&document.getElementById(currentId)===null){console.warn('Watermark removed, recreating...')currentId=setWatermark(str)}// 检测方式2:通过自定义属性双重验证(防止ID被篡改)constwm=document.querySelector('div[data-watermark="true"]')if(!wm){currentId=setWatermark(str)}},2000)// 窗口大小变化时,重新生成水印(自适应)window.onresize=()=>{// 仅当水印不存在时重建,避免频繁操作导致闪烁if(currentId&&document.getElementById(currentId)===null){setWatermark(str)}}},1000)// 延迟1秒,确保页面DOM渲染完成}exportdefaultwatermark以 Vue 2/3 项目为例,我们在根组件App.vue中引入并使用水印工具,实现全局水印效果:
<template> <div id="app"> <!-- 路由视图 --> <router-view /> </div> </template> <script> // 引入水印工具类 import watermark from "@/utils/watermark" export default { name: "App", components: { ThemePicker }, // 挂载完成后初始化水印 mounted() { // 设置水印内容:这里为“测试环境”,可根据环境动态传入(如生产环境、开发环境) watermark.set("测试环境-前端无涯") } } </script> <style scoped> /* 示例样式:隐藏主题选择器,可根据项目情况删除 */ #app .theme-picker { display: none; } </style>本文的水印方案相比传统实现,做了以下关键优化,提升了健壮性和兼容性:
wm-+ 随机字符串生成,避免被攻击者通过固定 ID 直接删除。data-watermark="true"检查,即使 ID 被篡改,也能检测到水印被删除。document.documentElement:相比挂载到body,documentElement(html 标签)更稳定,不会因body的样式(如overflow: hidden、position: relative)导致水印显示异常。toDataURL方法,防止因页面 CSP(内容安全策略)限制导致水印生成失败。pointer-events: none:水印容器设置该样式,不会遮挡页面元素的点击、hover 等交互事件,不影响用户操作。position: fixed+100vw/100vh,确保水印覆盖整个视口,且跟随窗口滚动。水印容器设置display: block !important和visibility: visible !important,防止被攻击者通过样式隐藏水印。
在实际使用中,可能会遇到以下问题,可参考对应解决方案:
can.toDataURL('image/png'),查看是否生成有效的 Base64 字符串。toDataURL is not allowed,需在服务端配置 CSP 策略,允许data:协议。position:确保为fixed,而非absolute(absolute 会受父元素定位影响)。window.onresize事件,在窗口缩放时重新生成水印,若仍出现错位,可将resize事件的处理逻辑改为立即重建水印(而非仅在不存在时重建):window.onresize=()=>{currentId=setWatermark(str)// 直接重建}根据实际需求,可对水印工具进行扩展:
watermark.update(str)方法,支持运行时动态修改水印文字。本文实现的网页水印方案,通过 Canvas 生成水印图片,结合 DOM 监听和定时检测机制,实现了防删除、自适应、高兼容性的核心需求。该方案已在 Vue 项目中验证,可直接复用至 React、Angular 等前端框架,也可用于纯 HTML/JS 项目。
在实际项目中,可根据环境(如开发、测试、生产)动态传入水印内容,进一步提升水印的实用性。同时,建议结合后端接口,获取用户信息(如用户名、工号)作为水印内容,实现更精细化的版权保护。