首页 profile 底部的海面由 JS 逐帧驱动:requestAnimationFrame 每帧重建 SVG 路径的 d 属性,波峰随相位真实流动,不是 CSS 平移的假动画。记录实现参数和两个坑。
波浪合成
海面三层(远/中/近),每层的波形是双频率正弦叠加:
k1是主波空间频率(每段弧度),speed是时间频率,相位速度 =speed / k1(段/秒)- 次频是空间频率
k2,约主波的 1/3,用来打破”一模一样”的波浪 - 次频的时间系数必须是
speed * (k2 / k1):让次波和主波同速前进,波形作为整体平移。如果随手写speed * 1.23,次波会在主波上快速滑过,看起来像高频抖动
三层参数刻意拉开,避免”几乎同频”的复制感:
| 层 | base | amp | speed (rad/s) | k1 | 波长 |
|---|---|---|---|---|---|
| 远 | 180 | 8 | 0.3 | 0.20 | ~898 单位 |
| 中 | 160 | 15 | 0.85 | 0.34 | ~528 单位 |
| 近 | 126 | 30 | 2.4 | 0.70 | ~257 单位 |
大浪(swell)
每层有独立的涨落周期:远 14s、中 11s、近 8.5s,幅度 5/8/11。最初三层共用同一个 10s 大浪,但远中层的自身波幅只有 5-9 单位,被 7 单位幅度的共享大浪盖过——三层看起来就是跟着同一个节奏上下抖,于是改成各自独立。
主题色与波峰亮线
水色跟随每款主题的 --link:
.home-island { color: color-mix(in srgb, var(--link) 30%, transparent);}[data-scheme="dark"] .home-island { color: color-mix(in srgb, var(--link) 38%, transparent);}近层浪尖有一条 1.2px 亮线,跟随波浪:
.home-island path.home-island-foam { fill: none; /* 必须提优先级 */ stroke: color-mix(in srgb, var(--link) 48%, transparent); stroke-width: 1.2px; vector-effect: non-scaling-stroke;}两个注意点:SVG 被 preserveAspectRatio="none" 拉伸,描边要加 vector-effect: non-scaling-stroke 才不会变形;.home-island path { fill: url(...) } 会覆盖 fill="none" 属性,选择器要提到 path.home-island-foam 才不把开放折线填充成三角色块。
坑:视图切换后动画失效
首页 → 文章 → 返回首页,海面不动了。原因是 astro:before-swap 由 Astro 以 bubbles: false 派发在 document 上,监听 window 永远收不到。循环没被取消、初始化状态没被重置,返回后 astro:page-load 想重新初始化又被状态挡住。改成挂 document 即可,和项目里其他脚本一致。
另外初始化加了守卫,astro:page-load 和模块立即执行可能双触发,只初始化一次;inited 要在所有前置检查通过后才置位,否则视图切换时脚本先于 DOM 换入执行,找不到目标元素就提前返回,状态却已锁死。
减动
prefers-reduced-motion: reduce 下不跑循环,只渲染一帧静态波形。所有动效都有这个兜底。
评论
GitHub 登录后可评论。
评论区会在滚动到这里时自动加载。