万花筒:拖一下就换一张壁纸,对称图案越转越上瘾

举报
deli007 发表于 2026/09/23 11:23:37 2026/09/23
【摘要】 万花筒:拖一下就换一张壁纸,对称图案越转越上瘾

万花筒最上瘾的地方是“随便一动就完全变样”。这个网页版把旋转、扭曲、缩放和配色都做成即时响应:按住拖动能转出不同角度,上下拖改变变形程度,滚轮或双指可以缩放,双击立刻复位。340 颗发光粒子按 8 重对称反复映射,任何一帧停下来都像一张对称壁纸,还能一键导出 PNG。下面先看成品,再讲这个单文件应用是怎么让码道 WebUI 生成并部署出来的。

万花筒 运行效果

为什么值得看

万花筒没有用一张张贴图,而是只维护 340 个粒子的极坐标,再把每个粒子的位置按扇形折叠、按 8 重对称展开。这样一笔画下去,实际上同时画出了 8 条对称线,几何图案天然整齐:

  • 8 重对称:emplace() 先调用 mapToSector() 把任意角度折进一个扇形,再围绕中心展开 8 份。
  • 拖尾与波折:上一帧点 P0 到当前帧点 P1 连线,粒子半径、自旋和波动幅度持续变化,拖尾不会断。
  • 配色即换:内置 7 套霓虹、极光、熔岩、薄荷等配色,按钮每点一次换一套。
  • 三种操控:拖拽旋转/扭曲,滚轮或双指捏合缩放,双击复位,移动端也能玩。
  • 导出 PNG:canvas.toBlob() 直接下载当前画面,时间戳做文件名。

任务描述

在码道 WebUI 输入框粘贴以下需求,回车发送:

我依然只描述最终效果,没有手写每一步。它这轮的动作是:

- 新建项目目录并写入单文件 `index.html`,随后又做几次增删改,把对称映射、缩放和导出逻辑补齐。
- 从页面里提取内嵌 JavaScript,用 `node --check` 做语法检查,确认没有语法错误。
- 探测空闲端口(这轮是 3000),用 `python3 -m http.server` 启动静态服务,`curl` 自检 HTTP 200 后给出内部预览地址。
- 最后把功能清单、文件位置和访问地址一起写回对话,预览地址就在对话末尾。

码道实际做了什么

开发一个“万花筒”网页应用:深色夜空背景,中心用大量彩色发光粒子同时呈现 8 重对称图案,粒子带连线拖尾并持续流动;支持鼠标拖拽旋转与改变形态、滚轮或触控双指缩放、双击复位;内置多套配色并支持切换;提供“导出 PNG”按钮,把当前画面保存为图片;界面简洁美观,单个 index.html 即开即玩。完成后自动部署成可访问的网页应用,并把访问地址写出来。

1. 对称的核心:先把角度折进一个扇形

如果直接按屏幕坐标复制 8 份,图案会到处乱飞。这里先把任意角度归一化后折成 0 到 sector 之间的基础角,再把两侧镜像补回来:

function mapToSector(ang, sector) {
  ang = ((ang % TAU) + TAU) % TAU;
  var seg = Math.floor(ang / sector);
  var a = ang - seg * sector;
  if (seg % 2 === 1) a = sector - a;
  return a;
}

function emplace(ang, rad, cx, cy, sector, out) {
  var base = mapToSector(ang, sector);
  for (var k = 0; k < symmetry; k++) {
    var theta = rotate + k * sector + (k % 2 === 1 ? (sector - base) : base);
    out[k * 2] = cx + Math.cos(theta) * rad;
    out[k * 2 + 1] = cy + Math.sin(theta) * rad;
  }
}

rotate 再叠加在展开角上,所以只改一个角度值,整幅对称图就一起转起来。

2. 暗底覆盖 + lighter 叠加,才有流光感

每帧不擦干净画布,而是铺一层几乎透明的深色,再用 lighter 合成模式叠加发光粒子,上一帧的痕迹自然形成拖尾:

ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = 'rgba(5,6,15,0.30)';
ctx.fillRect(0, 0, W, H);
ctx.globalCompositeOperation = 'lighter';

粒子的波折也不靠复杂曲线:在当前半径上乘一个随粒子角度和时间变化的正弦项,画面就像一直在“呼吸”。

3. 拖动和捏合用的是同一个目标值

控件不直接写死当前值,而是改 targetRotate / targetWarp / targetScale,动画循环里用插值因子慢慢逼近,所以松手后运动的收尾是平滑的:

canvas.addEventListener('pointermove', function (e) {
  if (!dragging) return;
  var dx = e.clientX - lastX;
  var dy = e.clientY - lastY;
  lastX = e.clientX;
  lastY = e.clientY;
  targetRotate += dx * 0.0065;
  targetWarp = clamp(targetWarp + dy * 0.012, 0.05, 3.2);
});

双指捏合时,还专门记录上一帧两点距离,用距离比换算成缩放变化。

验证与踩坑

生成结束后打开对话里的内部预览地址,页面标题是「万花筒 Kaleidoscope」,粒子图案确实在持续流动。

  • 隔几秒连续截两张运行画面,两个文件内容不重复:粒子半径、自旋和波折都有随机性,画面本来就在变。
  • 踩坑:内部预览链接偶发跳到开发者空间登录跳转页,重新打开一次才稳定进入应用;截图前先确认 Canvas 已经出现,避免截到空白页。
  • 从代码看,拖尾浓淡由 rgba(5,6,15,0.30) 的 alpha 决定;这个值越低,屏幕越难“擦干净”,拖尾越长,也更容易糊成一片。这是最值得调的一个参数。

本地复现

项目是单文件 index.html,没有第三方依赖。把文件保存到本地后用浏览器直接打开就能玩;如果希望通过本地 HTTP 服务访问:

cd codearts-kaleidoscope
python3 -m http.server 8080
# 然后打开 http://127.0.0.1:8080

直接下载试玩

源码同步到公开 Demo Park 仓库:

准备环境:进入 AI 开发者空间

本文用码道 WebUI 版演示。先进入 AI 开发者空间并选择体验版,不需要本地安装:

  1. 浏览器打开 https://devstation.developer.huaweicloud.com/aidevspace?utm_source=opcsfsq&utm_medium=wbwz
  2. 弹窗「请选择要进入的版本」→ 点击 体验版
  3. 首次使用跳到华为云登录页,登录一次后浏览器会话可复用
  4. 弹窗「临时凭证自动配置」→ 点击 同意,沙箱自动创建

进入主界面后,等左侧菜单加载出来,找到 CodeArts,点它右侧的浏览器图标,就打开了码道 WebUI 对话窗口。

AI 开发者空间主界面

开通后有三种使用方式:WebUI(浏览器对话)、TUI(终端命令行)和桌面 IDE(IDE 插件)。本文用 WebUI 版演示。

进入 CodeArts:https://developer.huaweicloud.com/codeartsco.html?source=dmzntgwsf&sourcead=dmzntgwwbwz

码道 WebUI 生成完成

使用码道体会

最省事的是「对称展开」这段:如果自己写,很容易在奇偶扇形镜像上犯晕,把条件写反后图案就会出现断线。让它直接在需求里点出“8 重对称 + 拖拽变形态”,生成出来的 mapToSector 和 emplace 分工很清楚,一个负责折角,一个负责展开,读起来比临时拼的映射公式直观。导出 PNG 这一步也顺手做掉了:canvas.toBlob() 相比 toDataURL() 对大画布更友好,实际点导出几乎没有停顿。

总结

万花筒的视觉密度来自“少维护、多展开”:340 个粒子通过 8 重对称变成约 2720 段连线,加上拖尾和 lighter 叠加,画面就很满了。后续可以继续加「对称数调节」「下载高清大图」「把某帧定格成壁纸」或「用种子生成固定图案」,让它从随手玩的小工具变成真正能产壁纸的生成器。

万花筒 运行效果对照

连续截取的两个运行画面,左边是初始图案的某个时刻,右边是拖动旋转、改变形态后的另一帧:

万花筒 连续运行画面

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。