「遥远未来的故事」的第一版背景,其实只有一片纯黑。

后来我觉得,一个关于星海的站点,至少应该让人听见一点夜的声音。于是我关掉 Canvas,试着只用手写 CSS 完成它。

凌晨两点的提交

一、把星星交给 box-shadow

如果每一颗星都是一个 DOM 节点,页面很快就会变得沉重。更轻的做法,是让一个 1px 的小圆点,用多重 box-shadow 复制出整片星野:

.stars {
  width: 1px;
  height: 1px;
  border-radius: 50%;
  background: #fff;
  box-shadow:
    12vw 18vh 0 rgba(255, 255, 255, .8),
    28vw 42vh 0 rgba(255, 255, 255, .5),
    43vw 12vh 0 rgba(255, 255, 255, .7),
    67vw 63vh 0 rgba(255, 255, 255, .4);
  animation: twinkle 7s ease-in-out infinite alternate;
}

坐标写成 vw / vh,星星就会跟着视口一起呼吸。再叠加一点点模糊,它们看起来就不再像坐标,而像光。

二、让光有层次

只有白色星点还不够。我会再叠两层:

  • 一层很淡的径向渐变,当作银河;
  • 两个大范围的模糊光斑,缓慢漂移,当作极光。
.aurora {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: .3;
  animation: drift 26s ease-in-out infinite alternate;
}

关键是「慢」。背景动效一旦快起来,就会从星空变成故障现场。26 秒、40 秒,甚至更久,反而更接近夜晚。

三、给流星留一条路

流星不是随机出现的。它需要一条足够长、足够斜的轨迹,以及一个不会一直重复的节奏:

@keyframes shooting {
  0%   { transform: translate3d(0, 0, 0); opacity: 0; }
  8%   { opacity: 1; }
  35%  { transform: translate3d(-36vw, 28vh, 0); opacity: 0; }
  100% { transform: translate3d(-36vw, 28vh, 0); opacity: 0; }
}

两枚流星使用不同的延迟与时长,于是它们不会一起出现。偶尔抬头看见一条光划过去,刚刚好。

四、最后的克制

这套背景的全部代码加起来不到一百行,没有图片请求,也没有额外的 JS 循环。

也是写到后来才明白:氛围不是把效果堆满,而是知道哪里应该留黑。

星星、极光、流星都只是背景。真正要发光的,是读故事的人停下来的那几秒。

这一篇读完了。愿你在某个遥远未来,与想见的人重逢。

← 返回文字档案