我在 React Canvas 里做了一个液态玻璃透镜效果

我在 React Canvas 里做了一个液态玻璃透镜效果

最近我在 react-canvas 里做了一版「液态玻璃透镜」后处理,目标是:

  • 透镜中心有放大感
  • 透镜边缘有明显的玻璃扭曲和轻微色散
  • 不引入第二套渲染栈,直接复用现有 CanvasKit 渲染管线

这篇文章记录一下这个效果的实现思路、关键代码和踩坑。

项目与线上地址

为什么要做成后处理(Post Process)

一开始看起来最直觉的方案是:在页面上叠一个额外 canvas 或 WebGL 层专门做透镜。

但这会带来两个问题:

  1. 两套渲染结果同步成本高(滚动、缩放、相机状态都要对齐)
  2. 交互命中(pick)和视觉效果容易打架

最终采用的是:在同一条 CanvasKit 管线中做全屏后处理

流程非常直接:

  1. 先把场景完整绘制到离屏 surface
  2. 对离屏图像应用 SkSL RuntimeEffect
  3. 再把结果一次性绘制到主画布

这样做的好处是:

  • 场景逻辑完全复用,不需要维护双渲染树
  • 效果控制集中在 shader 和 uniforms
  • 与既有渲染架构兼容性高

透镜效果拆解

当前液态玻璃由 4 部分组成:

  1. 中心放大:透镜内部有明显放大
  2. 边缘扭曲:扭曲主要集中在外圈
  3. 色散(RGB 偏移):边缘轻微彩边,增强"玻璃感"
  4. 边缘高光 + 阴影:让透镜有实体感

其中我重点优化了一个点:
中心不需要扭曲,只要放大;扭曲集中在边缘约 20% 的环带。

具体做法是引入归一化半径 t,再用:

  • rim = smoothstep(0.8, 1.0, t) 作为边缘权重
  • 中心(t < 0.8)扭曲权重接近 0
  • 靠近边缘(t -> 1)扭曲和色散逐渐增强

这样视觉上更像"镜片边缘折射",而不是整块区域都在抖动。

动画与性能:从"永远重绘"到"按需重绘"

透镜是纯 uniform 驱动的后处理,如果每帧都强制重绘,虽然简单,但成本偏高。

我把策略改成了:

  • 指针移动时通过 requestCanvasRepaint(canvas) 主动唤醒
  • shouldContinueRepaint 只在透镜还在追目标点时继续下一帧
  • 透镜静止后自动停止连续重绘

这个改动对体感影响很明显:移动时顺滑,停下时不再空转。

坐标与边界处理

Web 端最容易出错的是坐标系和边界采样:

  • 指针使用 getBoundingClientRect() + backing store 比例换算到像素坐标
  • shader 采样 UV 做 clamp(0..1),避免边缘出现黑边或抽样越界伪影

这些看似小问题,实际会直接影响"高级感"。

我这次的收获

  1. 视觉特效里,"哪里不做效果"往往和"做什么效果"一样重要
  2. 透镜这种效果,中心稳定 + 边缘强表达,比全域扭曲更自然
  3. 架构层做对(统一后处理管线)之后,后续微调成本会非常低

如果你也在做 Canvas/Skia 场景里的玻璃、景深、调色类效果,我非常建议优先走「场景离屏 + RuntimeEffect」这条路,扩展性会好很多。

相关推荐
又吹风_Bassy2 天前
WhatsCanvas教程-第十一章:性能优化
性能优化·canvas·skia·2d渲染库·nanovg·whatscanvas·2d图形库
又吹风_Bassy2 天前
WhatsCanvas教程-第九章:窗口呈现与交互
canvas·opengl·skia·2d渲染库·nanovg·whatscanvas·2d图形库
用户307140958483 天前
零依赖实现「形切形」:我用 6200 行原生 Canvas 写了个网页设计工具
前端·ai编程·canvas
又吹风_Bassy5 天前
WhatsCanvas教程-第三章:画笔 Paint 详解
canvas·opengl·skia·2d渲染库·nanovg·whatscanvas·2d图形库
又吹风_Bassy5 天前
WhatsCanvas教程-第二章:基础图形绘制
canvas·opengl·skia·2d渲染库·nanovg·whatscanvas·2d图形库
我就是DaLing呀!5 天前
vue3 + 独立的数据管理 Store实现视频剪辑功能
前端·typescript·vue3·canvas·store
牧艺9 天前
登录页还在用渐变?我一口气做了 5 个能摸的背景动效:吹蒲公英、滴墨染水、点熔岩闷裂
前端·canvas·交互设计
Highcharts.js9 天前
Highcharts 渲染方式解析:SVG 默认渲染与 Boost 模块的 WebGL 加速,以及它与其他可视化库的不同
svg·webgl·可视化·canvas·highcharts·渲染方式
AndrewHZ12 天前
图像处理入门018 | 简易图像浏览器开发:用 OpenCV + Tkinter 打造你的第一个图像 GUI 工具
图像处理·人工智能·opencv·canvas·tkinter·事件绑定
进哥AI研习社15 天前
WebGL 与视频背景——首屏视觉层的性能博弈
性能优化·webgl·gpu·canvas·shader·帧率·视频背景