今天折腾的是博客页脚上那条粉色虚线。
一开始的目标很简单:把 Reimu 主题页脚里的粉色线段换成站点里已经使用的团子图标,再让它动起来。听起来只是改一段 CSS,实际做下来却踩了不少前端动画里常见的坑:背景平铺、循环复位、DOM 生成时机、元素裁切、动画相位,以及“看起来像动了”和“真的自然动了”之间的差别。
先确认目标元素
最开始犯的错误是改错对象。
Reimu 页脚里有两个很像“页脚装饰”的东西:一个是版权文字旁边的小旋转图标,另一个才是上方那条粉色虚线。版权图标对应的是配置里的:
1 | footer: |
而真正要改的是页面里的 .footer-line。这一步确认之后,后续所有改动都只围绕 .footer-line 展开,不再动 footer.icon,避免影响 ©2026 凌 左边那个图标。
背景图替换跑通
第一步是最基础的替换:用 /images/2.png 覆盖原来的粉色线段。
1 | .footer-line { |
这一步的价值不是最终效果,而是验证三件事:选择器是对的,图片路径是对的,Hexo 生成后的页面确实加载了新样式。只有这一步成立,后面才值得继续调动画。
背景平铺的问题
直接用 background-repeat: repeat-x 的问题很快出现了:图标太密,看起来像一整条复制出来的素材。
尝试过调 background-size,但这里有一个容易忽略的点:background-size: 56px 24px 并不是“图标宽度 24px、间距 32px”,而是把图片本身拉伸到 56px × 24px。结果就是图标被压扁。
后来又试了:
1 | background-repeat: space no-repeat; |
它能保证图标不变形,但 space 会把背景图尽可能平均塞满整条线,视觉上还是偏密,而且移动时很难控制节奏。
结论是:如果需要“图标正常大小,并且每两个图标之间空一个图标宽度”,只靠背景平铺不够稳定。
改成独立图标
比较稳定的方案是让 .footer-line 里生成真正的图标元素,而不是继续用背景图模拟。
CSS 负责布局和显示:
1 | .footer-line .reimu-footer-static-icons { |
JS 只负责按宽度生成合适数量的图标:
1 | function renderFooterStaticIcons() { |
这版解决了两个关键问题:图标不再变形,间距也能稳定控制。
加入物理跳动
静态效果稳定之后,再加跳动。
图标本身用 CSS 动画即可,不需要 JS 每帧控制。为了模拟“落地压扁、起跳拉伸”的感觉,关键是设置 transform-origin 到底部:
1 | .footer-line .reimu-footer-static-icon { |
跳跃关键帧后来调低了幅度,避免图标跳起来时被 .footer-line 顶部裁切:
1 | @keyframes reimuFooterIconBounce { |
translateY(-8px) 看起来更明显,但在实际页面里会出现顶部被切掉的感觉。改成 -5px 后,跳动还在,裁切感小很多。
波浪式跳动
如果所有图标都用同一个动画时间点,就会出现“整排一起跳”的机械感。更自然的做法是在生成图标时给每个图标写入不同的动画延迟。
1 | icon.style.setProperty('animation-delay', (-i * 0.09) + 's', 'important'); |
这里用的是负延迟。负延迟的好处是页面加载完成时,动画不会从第一帧整齐开始,而是立刻进入错峰状态。视觉上就像一排图标正在形成连续的波浪。
0.09s 是一个比较克制的值。调大到 0.12s,波浪会更明显;调小到 0.06s,整排会更接近同步。
长条移动
最后才加入横向移动。
单独给每个图标改 left 看起来很灵活,但实际容易出现一个问题:初始化或回收位置处理不好时,图标会全部挤到左边。更适合这个场景的方案是把所有图标放在一条长条里,让长条整体向左移动,图标在长条里原地跳动。
长条容器:
1 | .footer-line .reimu-footer-static-icons { |
移动一个完整周期:
1 | @keyframes reimuFooterStripMove { |
这里的 48px 正好是:
1 | 24px 图标 + 24px 间距 |
也就是说,长条每次只移动一个完整单元。只要图标排列足够长,循环点就不容易被看出来。
移动速度最后从 8s 改成了 4s:
1 | animation: reimuFooterStripMove 4s linear infinite ; |
这个速度更容易看出横向移动,同时不会太抢注意力。
这次调试的几个教训
背景图适合做简单装饰,但一旦需要固定间距、独立跳动、物理形变,就应该尽早换成真实 DOM 元素。继续用 background-size 硬调,很容易把图片拉伸变形。
整段素材循环最容易产生“复录感”。如果移动的是一整条背景或一整组图案,循环终点和起点只要稍微对不上,就会出现抽帧或复位感。把移动距离控制为一个完整周期,可以降低这种感觉。
动画不要一开始就写得太复杂。今天比较稳定的顺序是:先确认目标元素,再替换静态图标,再修密度和比例,然后加跳动,最后再加横向移动。每一步只解决一个问题,能更快判断 bug 来自哪里。
前端动画最终看的不是代码有多复杂,而是用户感知是否自然。一个 -8px 的跳跃在代码里没问题,但在真实页面里会被容器裁切;一个 0.09s 的错峰延迟很小,却能让整排图标从机械同步变成波浪节奏。
这条页脚虚线最后变成了一条会移动、会弹跳、会轻微压扁的团子长条。它不算复杂,但把 CSS 背景、DOM 生成、动画相位和循环边界都过了一遍,算是一次很完整的小型前端动画调试。
