在数据可视化大屏中,“水球图”(Liquid Fill Chart)绝对是展示百分比数据(如CPU使用率、完成度、剩余电量)的颜值担当。
大家最熟悉的莫过于 ECharts 的 echarts-liquidfill 插件。它简单、好用,但在这个“卷”视觉的时代,传统的 2D 扁平水球有时显得不够“震撼”。
今天我们来聊聊一个有趣的话题:如何用 RayChart 手搓一个真实的 3D 水球,并对比它与 ECharts 水球的区别。
raychart体验地址:https://chart3js.netlify.app

ECharts 的水球本质上是 2D Canvas/SVG 绘图。
它的“波浪”并不是真的流体,而是通过数学公式(通常是正弦函数 y = A * sin(Bx + C) + D)在 2D 平面上绘制出的闭合路径。通过不断改变相位(Offset),让曲线平移,从而产生“波动”的视觉错觉。
为了追求“透亮”、“像真水一样”的效果,我们必须引入 WebGL 和 PBR(基于物理的渲染)。
在我的开源项目 RayChart 中,我实现了一个 Liquid3D 组件。下面来看看它是如何“骗”过你的眼睛的。

一个真实的 3D 水球通常由三部分组成:
ECharts 很难做出的效果就是“玻璃感”。在 RayChart (基于 Three.js) 中,我们使用 MeshPhysicalMaterial,并开启 transmission(透光率)属性。
const shellMaterial = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
transmission: 0.9, // 90% 透光,像玻璃一样
roughness: 0, // 极其光滑
ior: 1.5, // 折射率,模拟玻璃/水晶
thickness: 0.5, // 厚度,产生真实的折射效果
transparent: true
});这让背景能够透过球体产生扭曲,质感瞬间拉满。
液体本身其实也是一个球体(SphereGeometry),但我们需要根据水位把它“切”开。
普通的 clippingPlanes 切出来是平的,没有波浪。所以我们需要写一点 Shader(着色器)。
秘籍: 使用 onBeforeCompile 修改标准材质,在 Fragment Shader 中加入 discard 逻辑。
// GLSL 片段着色器伪代码
float h = getWaveHeight(vWorldPosition.x, vWorldPosition.z); // 计算波浪高度
// 如果当前像素的高度 > 水位 + 波浪高度,就丢弃(不渲染)
if (vWorldPosition.y > uLevelY + h) discard;这样,原本完整的球体就被“切”出了一个起伏的边缘。
被切掉的顶部不能空着,需要盖一个“盖子”。这个盖子是一个高密度的 PlaneGeometry。
难点: 盖子的波动必须和球体的切口严丝合缝,否则会漏水。
解决方案:
// Vertex Shader: 让顶点随波浪起伏
vec3 pos = position;
float waveH = getWaveHeight(worldPos.x, worldPos.z);
pos.y += waveH; // 顶点置换
// Fragment Shader: 切成圆形
if (length(vUv - 0.5) * 2.0 > 1.0) discard;维度 | ECharts LiquidFill | RayChart Liquid3D |
|---|---|---|
渲染引擎 | Canvas / SVG | WebGL (Three.js) |
视觉维度 | 2D 平面 | 3D 空间 |
质感 | 色块、渐变、扁平阴影 | 折射、反射、高光、环境光遮蔽 |
交互 | 鼠标悬浮高亮 | 360度旋转、缩放、甚至可以晃动液体 |
性能开销 | ⭐ (极低) | ⭐⭐⭐ (中高,依赖 GPU) |
适用场景 | 普通报表、H5 页面、移动端 | 大屏可视化、数字孪生、高逼格演示 |
RayChart 项目正在探索更多这样的 3D 图表组件,希望能把 WebGL 的门槛降下来,让大家都能轻松用上“电影级”的图表。
***
觉得有趣的话,欢迎点赞收藏!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。