我在 React Canvas 中实现了液态玻璃镜头效果

发布: (2026年4月15日 GMT+8 18:24)
4 分钟阅读
原文: Dev.to

Source: Dev.to

Introduction

我最近在 react-canvas 中实现了一个 液态玻璃镜头 后处理效果,目标有三:

  • 镜头中心拥有清晰的放大感
  • 边缘拥有强烈的玻璃式畸变并伴随细微的色散
  • 不使用第二套渲染栈,完全复用现有的 CanvasKit 流程

GitHub 仓库:
Live demo:

Rendering Strategy

最直观的做法——添加一个独立的 canvas 或 WebGL 层——会带来高同步成本(滚动、缩放、相机状态必须保持一致),并可能导致交互拾取冲突。

因此,我选择 在同一 CanvasKit 流程中进行全屏后处理

  1. 将完整场景渲染到离屏 surface。
  2. 对离屏图像应用 SkSL RuntimeEffect
  3. 在一次绘制中把处理后的结果绘制到主 canvas。

Benefits

  • 场景逻辑完全复用;不需要双渲染树。
  • 效果控制集中在着色器代码和 uniform 中。
  • 与现有渲染架构高度兼容。

Lens Components

当前的液态玻璃镜头由四个部分组成:

  1. 中心放大 – 镜头内部的清晰放大。
  2. 边缘畸变 – 畸变集中在外环附近。
  3. 色散(RGB 偏移) – 边缘的细微颜色晕染,增强玻璃感。
  4. 边缘高光 + 阴影 – 赋予镜头真实的物理存在感。

Key Optimization

中心应 产生畸变;畸变应集中在外部约 20 % 的环形区域。

引入归一化半径 t,并按如下方式使用:

// GLSL / SkSL snippet
float rim = smoothstep(0.8, 1.0, t); // edge weight
// Near‑zero distortion weight at the center (t < 0.8)
// Gradually increasing distortion and dispersion as t → 1

这种做法模拟了真实镜头的边缘折射,而不是让整个区域晃动。

Rendering Loop

镜头是一个纯 uniform 驱动的后处理效果。虽然可以在每帧强制重绘,但这会不必要地消耗资源。

  • 在指针移动时,通过 requestCanvasRepaint(canvas) 主动唤醒渲染。
  • shouldContinueRepaint 仅在镜头仍在追踪目标点时继续重绘。
  • 一旦镜头稳定,连续重绘会自动停止。

结果:移动时平滑,停止后不再空转。

Common Pitfalls

  • 坐标映射:使用 getBoundingClientRect() 加上后备存储比例来转换指针坐标。
  • 边缘采样:在着色器中使用 clamp(0..1) 限制 UV 采样,避免出现黑边或越界伪影。

这些细节直接影响最终的打磨程度。

Takeaways

  • 决定 不在何处应用 效果往往和决定 在何处应用 同样重要。
  • 对于镜头效果,稳定的中心加上富有表现力的边缘比全区域畸变更自然。
  • 一旦架构正确(统一的后处理管线),迭代调优的成本会大幅降低。

如果你正在 Canvas/Skia 场景中构建玻璃、景深或色彩分级等效果,建议从 “离屏场景 + RuntimeEffect” 的思路入手。它在后续扩展时的可扩展性更好。

0 浏览
Back to Blog

相关文章

阅读更多 »

从大学作业到作品集核心 📂

使用 React + TypeScript 构建交互式 N-Queens 可视化工具 Amandeep Singh 4 月 5 日 react typescript algorithms visualization 5 条回应 添加评论 5 分钟前