drawImage必须等图片onload完成才能调用,因为图片加载是异步过程,src赋值后像素数据尚未就绪,此时调用会静默失败导致canvas空白;跨域图还需设置crossOrigin="anonymous"并确保服务端支持CORS,否则因安全策略被拒绝。

drawImage 为什么必须等图片 onload 完成才能调用
直接在 img.src 赋值后立刻调用 ctx.drawImage(img, 0, 0),大概率什么也不画——drawImage 静默失败,canvas 保持空白。这是因为浏览器异步加载图片,img 对象还没准备好像素数据。
必须监听 img.onload 回调,在里面执行绘制逻辑。跨域图片还要额外加 img.crossOrigin = "anonymous",否则即使加载完成,drawImage 也会因安全策略被拒绝(控制台报错 Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The image argument is a canvas whose origin-clean flag is false.)。
- 本地文件或同源图:只需
img.onload - CDN 或第三方图:必须加
img.crossOrigin = "anonymous",且服务端需返回Access-Control-Allow-Origin响应头 - 避免用
setTimeout等“猜时间”,不可靠
文字水印用 fillText 还是 createPattern?关键看抗裁剪能力
单点 fillText 水印(比如只在左上角写“©2026”)容易被截图、裁剪或缩放破坏;而用 createPattern 平铺小图水印(如斜向“CONFIDENTIAL”PNG),视觉干扰强、覆盖全图、难以一次性清除。
但 createPattern 不是万能的:它依赖一张预加载好的 Image 对象,不能直接传 URL 字符串;平铺前必须手动设置 ctx.globalAlpha 控制透明度(建议 0.08–0.15),否则太实会盖住原图细节。
立即学习“前端免费学习笔记(深入)”;
- 文字水印适合简短版权标识,用
ctx.font = "bold 24px sans-serif"+ctx.textBaseline = "middle"+ctx.fillText(text, x, y) - 图案水印适合防搬运场景,先
const pattern = ctx.createPattern(watermarkImg, "repeat"),再ctx.fillStyle = pattern+ctx.fillRect(0, 0, canvas.width, canvas.height) - 倾斜效果要配合
ctx.save()/ctx.rotate()/ctx.restore(),否则后续绘图坐标系错乱
toDataURL 导出时 jpg 和 png 参数差异直接影响水印观感
canvas.toDataURL("image/jpeg", 0.9) 和 canvas.toDataURL("image/png") 表面只是格式不同,实际对水印呈现影响很大:
- JPEG 有损压缩,半透明文字边缘易出现色块或模糊,尤其低质量(
0.7以下)时水印变糊甚至消失 - PNG 无损,保留 alpha 通道,文字/图案水印边缘干净,但文件体积通常大 2–3 倍
- 导出前若用了
ctx.globalAlpha或rgba()颜色,PNG 能完整保留;JPEG 会丢掉部分透明信息,转为近似灰阶 - 移动端 Safari 对 JPEG 的 alpha 支持不稳定,建议高保真场景统一用 PNG
移动端 DPR 导致水印模糊?canvas.width/height 必须按设备像素比缩放
在 iPhone 或高 DPI 屏幕上,直接设 canvas.width = img.width 会导致绘制模糊——因为 CSS 像素 ≠ 物理像素。浏览器把 1px CSS 宽度渲染成 2 或 3 个物理像素,但 canvas 默认只按 CSS 尺寸分配缓冲区。
正确做法是读取 window.devicePixelRatio,放大 canvas 缓冲区尺寸,再用 CSS 缩回显示尺寸:
const dpr = window.devicePixelRatio || 1-
canvas.width = img.width * dpr,canvas.height = img.height * dpr -
canvas.style.width = img.width + "px",canvas.style.height = img.height + "px" - 绘制时所有坐标(
fillText的x/y、drawImage的位置)也要乘dpr
漏掉这一步,水印文字在手机上看就是毛边、发虚,尤其小字号时几乎不可读。



















