大版本更新
一口气全面统一博客样式,移除部分冗余导航页面,更专注内容展示。
不因虚度年华而悔恨,不因碌碌无为而羞愧
把参考站 mmz 的首页搬进 Shirone:双层明信片收缩、五幕横滑、终幕放大文案、两列内容区、页脚与悬浮个人信息卡,以及一套 rAF 控制器怎么把这些互不相干的动画串成一个连续场景。
一直很喜欢参考站那套「一张明信片缓缓收缩,然后横向滑过五幕,最后放大落进正文」的叙事。它没有导航、没有卡片瀑布流,整个首屏就是一段可以被滚轮驱动的短动画。
这次把它完整搬进 Shirone 的 mmz 皮肤:新页面变体 src/themes/mmz/pages/Home.astro 负责舞台,src/themes/mmz/home-dialogue.ts 负责控制器,其余交给 CSS 变量。下面是几个关键取舍。

图上看着像整张明信片在缩小,实际是两层:外层固定尺寸的舞台 + 内层可变换的明信片。滚动进度只写一个变量:
1.mmz-postcard {2 transform: translate3d(var(--mmz-card-x), var(--mmz-card-y), 0)3 scale(var(--mmz-card-scale)) rotate(var(--mmz-card-rotate));4}控制器的职责只有一件事——根据滚动进度算出 --mmz-card-scale 等几个值,其余动画全部由 CSS 消费。这样做的好处是:
五幕就是五个 100vw 宽的段落,容器 display: flex,滚动进度换算成 translateX。每一幕内部再做各自的入场:图片位移、文字淡入、遮罩展开,进度都用同一个 0 → 1 映射。
这里踩过的坑是高度。横滑容器不能用 height: 100vh,否则移动端浏览器地址栏伸缩时会把下面内容顶飞。统一用 100lvh 并用 --mmz-stage-height 兜底,Swup 切页回来也不会跳。
最后一幕不再横移,而是把当前画面整体放大、压暗,让居中文案浮出来。滚过阈值后:
scale 到设定值,边缘用径向渐变收口;translateY 上浮 + 淡入;translateY(100%) 滑上来,接管后续滚动。
之所以让内容区「滑上来」而不是「淡入」,是因为淡入会让人感觉首屏还没结束;上滑则明确告诉访客:叙事结束了,现在可以正常读文章。
内容区只有四个模块:快速带你前往、公告、近期文章、我的动态。左右两列做成严格对称:
flex: 1,面板内部再用 display: flex; flex-direction: column;flex: 1 撑满剩余高度,内部 overflow-y: auto,列表再长也不会把对面那一列拉歪;早期版本里两列各用各的卡片底色,结果一列是白的、一列是偏灰的,摆在拼图纹理上像两块补丁。改成「无底色 + 细描边」后,纹理能透上来,观感统一了。
页脚保留了完整的备案信息:ICP 备案号、公安备案徽标与备案号、版权行、RSS / Atom / Sitemap、Powered by 与版本号。排版用 flex-wrap 让它在窄屏自然换行,徽标固定 0.875rem,不再挤压文字。
左上角是鼠标悬浮才展开的个人信息卡,它属于持久外壳,不在 #swup-container 里。所以它监听的是 Swup 的 page:view,而不是页面自己的生命周期——否则从首页切到文章页后,卡片会留在上一个页面的状态。

首屏下方接了一组扇形卡堆叠,默认展示第一个相册。交互分两段:
flipFromRect 飞到屏幕中央变成大焦点图,再点一次或按 Esc 收回。用的是 src/utils/motion.ts 里的 flipFromRect 和 revealIn,两条都是纯 CSS transition 驱动,并且遵守 prefersReducedMotion()——关掉动效时直接显示终态,不做飞行。
所有滚动相关的写入都收在一个 rAF 里:
scroll 事件只置脏标记,真正的计算放到下一帧;decode() 完成后再校准一次占位高度,避免图片晚到导致舞台高度算错;destroy 时统一移除监听、取消 rAF、断开 ResizeObserver。这套写法在 Swup 下尤其重要:页面被替换时如果还有 rAF 在跑,会对着已经卸载的 DOM 写样式。控制器导出 destroy,由页面在卸载钩子里调用。
复刻一个首页,难的从来不是某个动画怎么写,而是怎么让五段互不相干的动画读起来像一段。答案是把它们压缩成同一个滚动进度,再让 CSS 各自消费;控制器只负责算数,不负责表演。
老规矩,npx.cmd astro check 0 errors、Playwright 实拍核对、pnpm.cmd build 通过之后才上线。
生成精美分享图或复制链接,与更多人分享本文。
最后更新于 ,距今已过 0 天
部分内容可能已过时