首页修改记录 04:最近听歌、Last.fm 和一张转动的黑胶
最近听歌模块把 Last.fm 数据、专辑图、黑胶 SVG 和环形布局接在一起。它看起来像视觉模块,实际先是数据边界。
最近听歌是这次首页里最不像“资料馆”的模块,但它很适合个人网站。技术文章、摄影和书法比较正式,音乐可以补一点日常感,让首页不只是作品列表。
实现时我没有做一个固定歌单,而是接入 Last.fm 的最近播放记录。这样它会自己变化,不需要每次手动更新。
数据先于视觉
Last.fm 返回的数据并不完全稳定。有的歌没有专辑图,有的专辑图是默认占位图,有的曲目正在播放,有的曲目只有历史播放时间。
所以先写了一个小的解析层:
export interface LastfmTrack {
id: string;
title: string;
artist: string;
album?: string;
imageUrl?: string;
url?: string;
playedAt?: string;
nowPlaying: boolean;
}页面只拿 LastfmTrack,不直接碰 Last.fm 原始响应。这样 UI 可以专注处理几种明确状态:
- 有图片就显示专辑图。
- 没图片就显示音符占位。
- 正在播放就给轻微高亮。
- Last.fm 没配置或请求失败,就显示简短提示。
为什么不显示播放时间
早期版本把每首歌的具体播放时间也显示出来了,但视觉上太像日志。这个模块更像一张“最近听过什么”的小展台,不需要精确到几月几日几点几分。
所以现在只保留“正在播放”状态,其它歌曲只显示编号、封面、歌名和歌手。
黑胶用 SVG,不用图片
一开始我考虑过找一张黑胶图片,但那样会有几个问题:
- 明暗主题下不好统一颜色。
- 尺寸和阴影不容易和页面一起调。
- 唱片旋转时,图像纹理可能显得重复或假。
最后黑胶由 TurntableDisplay 里的 SVG/CSS 画出来。它不是精细拟物,而是抽象的唱片:圆环、纹路、中心标签和缓慢旋转。
核心思路很简单:
<svg viewBox="0 0 420 420">
<circle cx="210" cy="210" r="190" />
<circle cx="210" cy="210" r="72" />
{/* many subtle record grooves */}
</svg>唱片慢慢转,模块就有了“正在播放”的感觉,但不会抢过内容本身。
环形歌曲布局
歌曲列表如果竖着排,会像表格。参考唱片图之后,我把 10 首歌分到唱片两侧,按角度围绕唱片排布。
位置来自极坐标:
function polarPosition(angle: number, radius: number) {
const radians = (angle * Math.PI) / 180;
return {
left: `calc(50% + ${Math.cos(radians) * radius}rem)`,
top: `calc(50% - ${Math.sin(radians) * radius}rem)`,
};
}编号和歌曲信息不是同一个 flex 列表里的元素,而是围绕同一个圆心计算位置。这样它们和唱片之间才有“同心”的关系。
长歌名怎么处理
歌名和歌手名长度不可控。直接换行会把布局撑乱,强行截断又会丢信息。现在的做法是默认两行稳定排版,过长文本在 hover 或 focus 时横向滚动。
这类细节看起来小,但它决定了模块是否能长期承受真实数据。Last.fm 不会因为页面布局而给我短歌名,所以页面必须自己处理不规则内容。
小结
最近听歌模块看起来是视觉设计,实际先是数据设计。先把 Last.fm 的不稳定响应收敛成稳定结构,再决定怎么画唱片、怎么排歌曲、怎么处理缺图和长文本。
一个个人网站可以有一点趣味,但趣味最好来自真实内容。最近听歌正好符合这一点:它不是装饰,它每天都在变。

