Canvas放大镜核心是“截取—缩放—贴图”三步闭环,需统一四个坐标系、用drawImage九参数精准裁剪、通过destination-out挖孔+source-over覆盖实现镜片效果,并用requestAnimationFrame节流重绘。

用 Canvas 实现放大镜局部预览,核心是“截取—缩放—贴图”三步闭环,关键不在画得多,而在坐标算得准、重绘控得稳。
一、先搞定图像加载和坐标对齐
很多卡顿或偏移问题,根源是没区分清楚四个坐标系:浏览器视口、CSS显示尺寸、图片原始像素、Canvas绘图空间。必须统一用 img.naturalWidth / naturalHeight 作为真实像素基准,再结合 getBoundingClientRect() 换算鼠标相对于图片左上角的偏移值。
- 别直接用
e.offsetX / offsetY,它受 CSS 缩放影响,不可靠 - 检查
img.complete === true,并监听load事件,双保险防空图报错 - 若图片通过
URL.createObjectURL()加载,绘制完成后调用URL.revokeObjectURL()防内存泄漏
二、用 drawImage() 九参数精准裁剪与放大
放大镜不是整体拉伸图片,而是从原图抠一小块(源矩形),再把它画大(目标矩形)。drawImage 的九参数写法是唯一可控方式:
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)- 假设放大倍率是 3 倍,镜片半径为 100px,则源区域宽高 =
200 / 3 ≈ 66.7px,中心点需反推:sx = mx - 33.3,sy = my - 33.3 - 目标区域固定为
dx = mx - 100,dy = my - 100,dw = dh = 200,确保镜片始终居中显示 - 所有坐标必须用
Math.max(0, Math.min(...))限制在原图范围内,避免越界黑块
三、实现“挖孔+覆盖”的视觉镜片效果
Canvas 没有透明遮罩层,所谓“镜片”,其实是先擦除底图一块圆形区域,再把放大后的内容填进去:
立即学习“前端免费学习笔记(深入)”;
- 先用
ctx.globalCompositeOperation = 'destination-out'擦出一个圆(即让该区域变透明) - 立刻切回
'source-over',再用 drawImage 把放大区域画到同一位置 - 每次绘制前调用
ctx.save(),结束后ctx.restore(),防止 composite 模式污染其他绘制 - 可加轻微阴影增强立体感:
ctx.shadowBlur = 4; ctx.shadowColor = '#0003';
四、性能必须靠 requestAnimationFrame 节流
mousemove 每秒触发几十次,不节流必卡。不能在事件里直接重绘,而要标记状态、交由帧循环统一处理:
- 声明
let needsUpdate = false,mousemove 中只设为true - 用
requestAnimationFrame(renderLoop)启动循环,renderLoop 内检测needsUpdate,执行清空→底图→挖孔→放大绘制 - 镜片区域小(建议 ≤250×250),离屏 canvas 预渲染放大图,移动时只平移不重缩放,大幅降耗
- 移动端要监听
touchmove,取touches[0],并e.preventDefault()阻止滚动干扰



















