挑战一:粒子密度与性能的平衡

最初的粒子数量设为 8000,导致页面打开时渲染管线严重卡顿。经过逐次二分降采样测试,我们将沙丘粒子降至 2200——此时在 60fps 目标下,GPU 填充率刚好保持在 WebGL 绘制调用预算内。核心发现是:视觉丰富度并不线性依赖于粒子数量——2200 个间距合理的粒子,通过增大单粒子半径并降低全局不透明度,反而比 8000 个密集粒子看起来更加"透气"和优雅。

挑战二:Canvas 纹理的语言切换

卡牌的正面标签和背面箴言是绘制在 Three.js CanvasTexture 上的。语言切换时,不能简单地替换 DOM 文本——必须重新创建 Canvas、重新绘制(含异步图片加载)、销毁旧纹理、替换材质引用。这个过程中需要小心处理 的 onload 时序:我们在 createFloatingCardTexture 中采用了"立即绘制 + onload 补绘"的双保险策略,确保无论图片是否缓存命中,标签文本都能立即以当前语言呈现。

挑战三:Z-fighting 与卡牌双面渲染

双面卡牌(正面图片 + 背面品牌)在深度测试中容易产生 Z-fighting 条纹伪影。解决方案是:正面 Mesh 偏移 +0.06z,背面 Mesh 偏移 −0.06z−0.06z,并在中间放置一个透明度 0.9 的微边框 BoxGeometry——三层几何体通过 00.12z 的总间距彻底消除了 Z-fighting,同时保持了视觉上的"纸张厚度感"。

挑战四:单文件拆分的依赖拓扑

将 1900 行单文件拆分为 6 个 JS 模块时,最大的难题是全局变量(scene, cardsArray, gameState, focusedCard 等)在文件间的共享。由于项目未使用构建工具(保持零依赖的纯前端体验),我们采用 标签的加载顺序作为隐式依赖图:G={i18n→data→audio→three-scene→ui→main} 这种拓扑保证了每个文件执行时,其依赖的全局函数(如 getTagI18n, t(), audioManager, createCards())已完成注册。</p> <p>反思 · Reflections 构建 Three Pictures 的过程,本身就是一场&quot;慢下来&quot;的练习。 在追求极致性能与交互流畅度的同时,我们不断提醒自己:这个项目的灵魂不在于技术堆砌,而在于它能否让某个深夜打开它的人,在蒲公英飘过屏幕的那一刻,想起一张被遗忘的照片——想起照片里那个人的笑意比阳光更耀眼。 技术是载体,情感才是内容。 这也是为什么我们把&quot;卡牌微距阻尼拖拽&quot;的 ease: &quot;power1.out&quot; 调了七个版本——不为了性能,只为了手指松开那一刻,卡牌继续滑行的那一点点惯性,恰好就是记忆在脑海中消散的速度。</p> <p>&quot;让每一个不经意的画面,随风汇聚,融化成彼此眼中的星火。&quot;</p>

Built With

Share this project:

Updates