canvas绘制倒影需先将原点移至图像底边中点,再缩放翻转并精准绘制,配合渐变遮罩和透明度控制实现真实效果,避免错位的关键是平移在缩放前执行。

Canvas 绘制倒影效果,核心不是“画两遍图”,而是用坐标系变换把原图垂直翻转后精准“摆”在它下方,并叠加渐变遮罩模拟真实倒影的虚化感。直接调 drawImage 加 scale(1, -1) 很容易错位或消失,关键在于原点移动和绘制坐标的配合。
倒影绘制必须绕图像底部中心翻转
倒影是原图关于水平线(通常是图像底边)的镜像。不能以画布左上角为原点翻转,否则图像会向上跑出可视区。正确做法是:先将坐标系原点移到图像底边中点,再缩放,最后绘制。
- 设原图左上角为
(x, y),宽w、高h,则底边中点为(x + w/2, y + h) - 翻转前调用:
ctx.translate(x + w/2, y + h) - 再执行:
ctx.scale(1, -1) - 此时绘图起点变为
(-w/2, 0),所以用ctx.drawImage(img, -w/2, 0, w, h)才能对齐 - 完成后记得
ctx.restore()或ctx.setTransform(1,0,0,1,0,0)清除状态
倒影区域要预留空间并控制透明度
画布高度必须大于原图高度,否则倒影会被裁掉。建议总高度 = 原图高 × 2.2(含间距与渐变余量);倒影本身不单靠 opacity,而要用线性渐变遮罩增强真实感。
- 设置倒影不透明度:
ctx.globalAlpha = 0.65(初步压暗) - 创建从倒影顶部向下渐变的遮罩:
const grad = ctx.createLinearGradient(0, y + h, 0, y + h + h * 0.8) - 添加色标:
grad.addColorStop(0, 'rgba(255,255,255,0)')和grad.addColorStop(1, 'rgba(0,0,0,0.25)') - 用该渐变填充一层矩形覆盖倒影区域,模拟水面消散效果
避免常见错位:平移+缩放顺序不能颠倒
很多白屏或半张图问题,都源于 scale(1,-1) 和 translate 的调用顺序错误。记住:翻转前必须先把原点移到翻转轴上,而不是翻转后再移。
- ✅ 正确顺序:
save()→translate(...)→scale(1,-1)→drawImage(...)→restore() - ❌ 错误写法:
scale(1,-1)后直接drawImage(x,y,...)—— 此时y坐标含义已反转,数值不变就必然错位 - 若用
setTransform精确控制,垂直翻转并定位到底部可写:ctx.setTransform(1,0,0,-1,0,2*(y+h))
性能优化:复用 canvas 与预渲染倒影
频繁重绘倒影影响帧率,尤其在滚动或动画场景中。推荐提前生成倒影 canvas 实例,后续只做合成绘制。
- 创建离屏 canvas:
const offscreen = document.createElement('canvas'),尺寸同原图 - 在其上一次性完成翻转+渐变绘制,得到倒影图像数据
- 主 canvas 中直接
drawImage(offscreen, x, y + h, w, h * 0.9)复用,省去重复变换开销 - 跨域图片务必设置:
img.crossOrigin = "anonymous",否则离屏读取会失败











