壁纸、主题和播放器是怎么做的

技术设计

这个站上「换壁纸」「切深浅色」「放歌」这三件事,都不需要服务器。它们的共同点是:状态只属于当前访客,存本地就够了。

壁纸:CSS 渐变而不是图片

四张壁纸全部是 CSS 渐变,没有一个图片文件。这样做有几个好处:任意分辨率都清晰、整站体积小、不会有版权问题。

壁纸定义在 src/data/site.ts 里:

{
  id: 'aurora',
  name: '极光',
  dark: "radial-gradient(1200px 600px at 12% 8%, rgba(56,189,248,.30), transparent 60%), linear-gradient(160deg, #04070f, #0f2236)",
  light: "radial-gradient(1200px 600px at 12% 8%, rgba(56,189,248,.45), transparent 60%), linear-gradient(160deg, #eef6ff, #e8f1ff)",
}

布局文件会把它编译成一组 CSS 规则:

:root[data-wallpaper='aurora'][data-mode='dark'] #bg { background-image: ... }
:root[data-wallpaper='aurora'][data-mode='light'] #bg { background-image: ... }

于是切换壁纸只需要改 <html> 上的一个属性,浏览器自己会做过渡。

想换成自己的照片,把图片放进 public/wallpapers/,然后把 light / dark 改成 url('/wallpapers/photo.jpg') center / cover no-repeat 就行。

不让页面闪一下

如果等 JavaScript 加载完再读取偏好,用户会先看到默认壁纸闪一下再变。解决办法是把读取逻辑写成 <head> 里的内联脚本——它在浏览器画第一帧之前就同步执行完了:

<script is:inline>
  (function () {
    var root = document.documentElement;
    var saved = JSON.parse(localStorage.getItem('site-prefs') || '{}');
    if (saved.mode) root.dataset.mode = saved.mode;
    if (saved.wallpaper) root.dataset.wallpaper = saved.wallpaper;
  })();
</script>

is:inline 是 Astro 的指令,表示「不要打包这个脚本,原样输出到 HTML 里」。这是它和默认 <script> 的关键区别。

播放器:原生 audio 就够

播放器没有用任何第三方库,核心就是 new Audio() 那一套:play()pause()timeupdate 事件、currentTime 赋值。

几个必须处理的细节:

  • 不要自动播放。 浏览器会拦截没有用户交互的 play(),而且突然响起来也很讨厌。播放器只在用户点击后才出声。
  • 音量和进度要存。 存进 localStorage,下次打开还是你调好的样子。
  • duration 要等元数据。loadedmetadata 事件之前读 audio.duration 会得到 NaN
audio.addEventListener('loadedmetadata', () => {
  time.textContent = `0:00 / ${fmt(audio.duration)}`;
});

audio.addEventListener('timeupdate', () => {
  if (scrubbing) return; // 用户正在拖进度条时不要抢控制权
  seek.value = String(Math.round((audio.currentTime / audio.duration) * 1000));
});

音频文件放哪里

小文件可以直接丢进 public/music/。但一首 320kbps 的歌大约 8 到 10MB,攒上几十首,Git 仓库就会变得又大又慢——GitHub 还限制单个文件不能超过 100MB。

更合适的做法是把音频放到 Cloudflare R2:免费 10GB 存储,而且不收流量费。上传后拿到公开链接,直接填进 playlistsrc 就行。

最后提醒一句:放别人的商业音乐是有版权风险的,自己录的、原创的,或者明确标注 CC 授权的才安全。

0:00 / 0:00