水波纹:点一下水面就漾开,压力感瞬间被揉散
水波纹最解压的点在于“动一下立刻有回应”。这个网页把整块画布当成一片可触摸的水面:点一下会漾出一圈圈涟漪,按住滑动会连成一条水痕,开启雨滴模式后还有雨点随机落下、各自打出小水花。波纹强度、扩散速度和涟漪颜色都能调,右上角实时显示帧率。下面先看成品,再讲这个单文件应用是怎么用码道 WebUI 生成并部署出来的。

为什么值得看
它没有用大量 DOM 元素去做圆圈动画,而是把水面看成一块高度场:每个网格单元存一个数值,水波就是数值在相邻单元间来回扩散。这样点击、滑动、雨滴都只是在对应位置“压”一下,画面自然跟着物理规则漾开:
- 双缓冲高度场:
bufA和bufB来回交换,保证每一步同时更新,不串帧。 - 点击与滑动:点按产生空心涟漪,滑动按步长插值,连成一条连续水痕。
- 雨滴模式:开关一开,雨滴随机下落,落到水面上的位置调
splash()打水花。 - 三个可调参数:波纹强度、扩散速度、涟漪颜色都实时生效。
- 60 fps 显示:右上角按秒统计刷新率,方便看设备跑得顺不顺。
任务描述
在码道 WebUI 输入框粘贴以下需求,回车发送:
我仍然只说清楚最终效果。它这轮的动作是:
- 新建项目目录并写入单文件 `index.html`,后续调整数组大小和调色板,把性能与观感补齐。
- 提取内嵌 JavaScript 跑 `node --check` 语法检查,确认没有语法错误。
- 探测空闲端口(这轮是 3000),用 `python3 -m http.server` 起服务,`curl` 自检 HTTP 200 后给出内部预览地址。
- 最后把功能清单、文件位置和访问地址一起写回对话,预览地址就在对话末尾。
码道实际做了什么
开发一个“水波纹模拟器”网页应用:深色平静水面铺满窗口,点击或滑动水面会实时荡起涟漪,支持触摸操作;提供波纹强度、扩散速度和涟漪颜色三个调节项;内置雨滴模式,开启后雨点随机落下并溅起水花;右上角显示实时帧率,画面流畅、色彩治愈,单个 index.html 即开即玩。完成后自动部署成可访问的网页应用,并把访问地址写出来。
1. 点击和雨滴共用同一个扰动函数
点按用的是空心涟漪(能量集中在圆环上),滑动和雨滴用的是实心扰动,两种波形都从同一个 splash() 出来,只是幅度函数不同:
function splash(cx, cy, radius, strength, hollow) {
var x0 = Math.round(cx), y0 = Math.round(cy);
var r2 = radius * radius;
for (var dy = -radius; dy <= radius; dy++) {
var y = y0 + dy;
var row = y * W;
for (var dx = -radius; dx <= radius; dx++) {
var d2 = dx * dx + dy * dy;
if (d2 > r2) continue;
var x = x0 + dx;
var dist = d2 === 0 ? 0 : Math.sqrt(d2);
var amp;
if (hollow) {
amp = Math.max(0, 1 - Math.abs(dist - radius * 0.55) * 2.6);
} else {
amp = Math.cos(dist / radius * Math.PI * 0.8 + 0.3);
if (amp < 0) amp = 0;
}
cur[row + x] = clampAdd(cur[row + x], -strength * amp, -600, 600);
}
}
}
2. 高度场用有限差分推进,一次只算一步
水波的核心是让每个点向四周扩散。step() 取上下左右四个邻居的平均值乘一个扩散系数,再减去当前值;双缓冲在这里决定“新值与旧值互不干扰”:
v = (sp[i - iw] + sp[i + iw] + sp[i - 1] + sp[i + 1]) * 0.25 * d - si[i];
v *= damp;
if (v > 600) v = 600; else if (v < -600) v = -600;
si[i] = v;
每一步之后交换 prev 和 cur,所以下一次迭代读到的就是刚更新完的高度场。
3. 滑动不是一条线,而是拆成很多小水花
如果拖动距离长了只打一个点,水痕会断断续续。代码按鼠标移动距离计算步数,沿路径均匀插值,把一次滑动拆成连续多个 splash():
var dx = x - lastPt.x, dy = y - lastPt.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var steps = Math.max(1, Math.ceil(dist / 3));
for (var s = 1; s <= steps; s++) {
var px = lastPt.x + dx * s / steps;
var py = lastPt.y + dy * s / steps;
splash(px, py, 2 + params.strength * 3.5, 42 * params.strength, false);
}
验证与踩坑
生成结束后打开对话里的内部预览地址,页面标题是「水波纹模拟器」,点击画面确实会漾开涟漪。
- 分别截下初始平静水面和点击后的画面,两个文件内容不同:扰动强度不同,涟漪半径也不同。
- 踩坑:物理网格的分辨率会影响画质与流畅度。生成时会根据窗口面积在 24000 到 130000 个单元之间自适应选择 CELL,屏幕越小 CELL 越小、细节越密。
- 扩散速度调高后,
fDenom = 4 / (1.35 + params.speed * 1.25)会变小,单步更新更“硬”;拉满后水波会比较躁,喜欢柔和感的话默认 60% 最舒服。
本地复现
项目是单文件 index.html,没有第三方依赖。把文件保存到本地后用浏览器直接打开就能玩;如果希望通过本地 HTTP 服务访问:
cd codearts-water-ripple
python3 -m http.server 8080
# 然后打开 http://127.0.0.1:8080
直接下载试玩
源码同步到公开 Demo Park 仓库:
- Demo Park 仓库:https://atomgit.com/deli007/demo_park
- 本案例目录:https://atomgit.com/deli007/demo_park/tree/main/codearts-water-ripple
准备环境:进入 AI 开发者空间
本文用码道 WebUI 版演示。先进入 AI 开发者空间并选择体验版,不需要本地安装:
- 浏览器打开 https://devstation.developer.huaweicloud.com/aidevspace?utm_source=opcsfsq&utm_medium=wbwz
- 弹窗「请选择要进入的版本」→ 点击 体验版
- 首次使用跳到华为云登录页,登录一次后浏览器会话可复用
- 弹窗「临时凭证自动配置」→ 点击 同意,沙箱自动创建
进入主界面后,等左侧菜单加载出来,找到 CodeArts,点它右侧的浏览器图标,就打开了码道 WebUI 对话窗口。

开通后有三种使用方式:WebUI(浏览器对话)、TUI(终端命令行)和桌面 IDE(IDE 插件)。本文用 WebUI 版演示。
进入 CodeArts:https://developer.huaweicloud.com/codeartsco.html?source=dmzntgwsf&sourcead=dmzntgwwbwz

使用码道体会
最直观的收获是「高度场」这个抽象:它把二维水面压缩成一个个数值,波纹、雨滴和渲染三层逻辑拆得很开。尤其是双缓冲,一开始没太理解为什么需要两份数组,看到 step() 里邻居同时读取旧值才明白:如果边算边覆盖同一个数组,波会向已经更新过的一侧倾斜。需求里没提双缓冲,但它自己按稳定做法写出来了,这种不显眼但正确的细节比把按钮调漂亮更有价值。
总结
水波纹把一个很容易想象的物理现象落到高度场迭代:点击产生扰动,有限差分负责扩散,调色板负责观感。后面可以继续加「石头落下的大水花」「背景渐变」「点击音效」或「波纹定格导出」,也可以把高度场接到其它网格类效果上,玩法空间不小。
水波纹 运行效果对照
连续截取的两个运行画面,左右分别是平静水面与点击、滑动后的涟漪状态:

- 点赞
- 收藏
- 关注作者
评论(0)