我把网页上那团流动的彩色云雾,塞进了微信小程序

📅 2026/8/6 4:37:00
我把网页上那团流动的彩色云雾,塞进了微信小程序
合集 - 小程序(2)1.小程序瀑布流我用 170 行代码干掉了布局抖动07-252.我把网页上那团流动的彩色云雾塞进了微信小程序08-03收起之前我在网页上做过一个好玩的东西一颗胶囊形状的卡片里面的颜色像水彩一样不停流动鼠标划过去还会跟着加速。很多朋友问能不能在小程序里用。我试了才知道网页上三行代码搞定的 canvas到了小程序里完全是另一回事。这篇记录一下这团云雾从网页搬进微信小程序的过程以及我最后把它封装成的到小程序sk-flux-capsule。一、网页版是怎么做出来的它的效果是一个药丸形的白色胶囊里面封着一团彩色烟雾。烟雾不是视频也不是 GIF是实时算出来的。每一帧GPU 都在根据噪声函数重新计算每个像素的颜色所以它永远在动又永远不会重复。原理拆开看其实不复杂用 WebGL 画一个铺满胶囊的三角形所有颜色计算都写在片元着色器里交给 GPU 跑着色器里用的是域扭曲 FBM 噪声说人话就是两层噪声互相推挤挤出来的纹理就像水彩晕开每个主题传三个主色进去按噪声值在三者之间平滑过渡左侧留一块渐隐的白给文字顶部再蒙一层淡淡的白雾看起来就像 iOS 风格的玻璃胶囊。整个页面一个 HTML 文件没有任何依赖浏览器直接打开就能跑。鼠标悬停卡片流速会轻轻加快在胶囊里拖动烟雾会跟着你的手势翻涌停下后一秒左右平滑回落。网页版做出来我自己挺满意。问题随之而来能不能放进小程序二、为什么想搬进小程序原因很实际。网页版再好看也只能发个链接。而小程序是触手可及的用户不用装东西、不用记网址扫一下就在手里。我想做的组件库本来就是面向 uni-app 的这种有视觉记忆点的组件正是小程序里缺的。但真正动手才发现把网页代码复制过来这条路根本走不通但是如果使用vibe coding 方式迁移呢噪声算法、交互动力学一行都不许改只允许改画布获取方式 小程序端 canvas 必须用 SelectorQuery 异步取 node不能用 DOM API 帧循环必须用 node.requestAnimationFrame不能用 window.requestAnimationFrame 平台差异用 uni-app 条件编译#ifdef MP-WEIXIN / #ifdef H5全部收敛进一个 setupCanvas() 函数三、搬进小程序到底难在哪先给结论难的不是算法是画布本身。着色器那套逻辑一行没改改的全是画布的平台差异利用AI能最大化提高效率但实际也有很多小问题无非解决方法就是花费更多的token或者直接手动修复细节问题1. 小程序里没有你熟悉的 canvas网页上document.createElement(canvas)或者模板里写个canvas拿到的就是一个 DOM 元素getContext(webgl)直接开画。小程序里没有 DOM。它的 canvas 是一个原生组件你拿不到元素本身只能通过SelectorQuery去查查到的是一个canvas node对象。获取方式完全不同uni.createSelectorQuery() .select(#myCanvas) .fields({ node: true, size: true }) .exec((res) { const node res[0].node // 这才是能用的画布对象 })而且这个查询是异步的你没法在组件挂载的瞬间就拿到画布得等布局完成。2. 动画循环的驱动方式不一样网页上驱动动画用window.requestAnimationFrame全局可用。小程序里window不存在帧回调挂在 canvas node 上node.requestAnimationFrame。也就是说你得先拿到 node才能开始画第一帧。这两件事的先后顺序在网页上根本不用考虑。3. 一套代码要分两条路走uni-app 的条件编译在这里帮了大忙。同一份组件代码用注释标记区分平台/* #ifdef MP-WEIXIN */ // 小程序SelectorQuery 取 canvas node /* #endif */ /* #ifdef H5 */ // 网页动态创建原生 canvas /* #endif */编译到哪个平台就只保留哪段。我把所有平台差异都收敛进一个setupCanvas()函数函数之外的渲染逻辑、交互逻辑两端完全共用。4. 清晰度要自己管网页上 canvas 的像素尺寸和 CSS 尺寸不一致时会糊小程序上更明显。解决办法是按设备像素比DPR放大画布的物理分辨率避免高分屏上无谓地烧性能。5. 最担心的还是性能这是迁移前我最没底的一点。小程序的运行环境比浏览器受限得多WebGL 在里面跑不跑得动、会不会发烫掉帧只能实测。四、组件是怎么组织的踩完坑我把这个东西收进了sk-flux-capsule结构上把平台和逻辑彻底分开sk-flux-capsule/ ├── sk-flux-capsule.vue # 视图 平台差异setupCanvas 里分两条路 └── core/ ├── color.ts # 颜色解析CSS 字符串 → 着色器调色板 ├── motion.ts # 交互动力学按压、搅动、流速缓动 ├── renderer.ts # WebGL 渲染器编译、缓存、释放 └── shaders.ts # GLSL 着色器源码core里四个文件是纯逻辑不碰任何平台 API网页和小程序共用同一份。平台差异只存在于.vue文件里那一小段条件编译。这样做的好处是以后哪天要支持 App 或者别的端只需要在setupCanvas()里再加一条分支核心的着色器和动力学一行都不用动。用起来也很简单传几个颜色进去就行sk-flux-capsule :colors[#ff4f9e, #ff8c40, #b83dff] titleFLUX /单个颜色也可以组件会自动派生出明暗两个邻近色凑成有层次的三色sk-flux-capsule colors#5b8cff titleMONO /五、小程序里到底卡不卡这是大家最关心的也是我动手前最没底的。直接上微信开发者工具的 Audits 跑分几个关键数字性能评分 96体验评分 100都是优秀档运行时 setData 平均耗时 1ms说明渲染循环没有给逻辑层造成负担平均内存占用 118MB峰值 127MB对一个跑着 WebGL 的页面来说在可接受范围。实际手感上云雾稳定流动拖动跟手没有明显掉帧。WebGL 在小程序里跑得动这件事算是验证了。六、几个使用上的提醒页面里同时放太多实例会吃 GPU。演示页我一开始堆了十几个低端机明显发热后来收敛到一屏三四个。页面切后台记得停帧。组件暴露了pause()/resume()配合onHide/onShow用避免页面看不见的时候还在烧电。多个实例想纹理不一样传不同的seed。不传的话每个实例也会随机错开不会同步。七、最后这团云雾从网页到小程序算法一行没改改的全是画布的平台差异。把这部分差异收敛进一个函数之后剩下的事情就变得很纯粹。组件已经收进 shukelab如果你也在做小程序里有点视觉记忆点的东西希望这篇能帮你少踩几个 canvas 的坑。GitHub-webhttps://github.com/KTBOY/shuke-lab-fluxGitHub-小程序https://github.com/KTBOY/shukelab