同样被布置成展厅的一面墙上挂着这幅作品:六千多个星点从画面深处持续扑向镜头,越近越大越亮,掠过边缘后消失。鼠标移动会改变飞行方向,星流随之偏转;按住左键则明显加速,星点被拉成一道道光带,视野同时收窄,很像科幻片里进入曲速的一瞬。松开后速度和视野都缓慢恢复。技术上是原生 Canvas 逐像素绘制,透视投影与拖影都是手写的。
这个星空的三处做法
光带是两点连线。脚本记下星点上一帧的投影位置,本帧把这两点连成线,速度越快两点间距越大,拖影自然越长,不需要任何模糊或残影技巧。
加速时焦距同步收窄。速度上升时焦距从 300 降到 210,视野变窄、边缘星点向外加速逃逸,强化了冲刺感。
星点在镜头后会复位。深度越过镜头的星点不销毁,而是被送回最远处重新随机一个横纵坐标,所以星场可以无限穿行。
旋转是绕屏幕中心的二维旋转
按住鼠标时整片星场会转起来,实现方式不是三维旋转矩阵,而是把已经投影到屏幕上的坐标绕中心做一次二维旋转:
cos * (star.ox - center.x) + sin * (star.oy - center.y) + center.x
这是一个明确的取舍:真正的三维滚转需要对每个星点做矩阵运算,六千多个点的开销不小;而在「镜头朝正前方」的前提下,投影后再转屏幕坐标,视觉结果几乎一样,计算量却低得多。这类近似在实时图形里很常见,也是这份源码值得一读的地方。
能改的地方
星点数量、初始速度与加速上限、焦距上下限、星场深度都是脚本顶部的变量。星点颜色在像素写入处,由于是叠加写入,建议从偏暗的值往上调。展厅的墙面、画框、地面是纯 CSS 背景,不需要可整段删除,让画布铺满页面即可。
常见问题
全屏时帧率下降?
逐像素写入的开销与画布面积成正比。先缩小画布尺寸,再减少星点数量,这两项收益最直接;把拖影关掉(只画点不画线)也能省下不少。
手机上能用吗?
能。脚本会检测移动设备并改绑触摸事件,同时自动降低星点数量。
可以把速度接到别的东西上吗?
可以。速度只是一个变量,接到滚动位置、音频强度或页面加载进度上都成立,其余逻辑不用改。
适合用在哪
科幻与游戏站点的首屏、产品发布会的开场动画、加载与跳转过渡页,也是一份不错的「透视投影加拖影」实现参考。投影换算、拖影连线与复位机制的完整代码都在脚本里,下载后可查看。