前端踩坑续篇:动效、事件与布局的十个补记
Ten follow-up notes from building this blog: production CSP, hydration, event phases, animation parameters, and the layout experiments I eventually simplified.
前面写过十四个博客开发踩坑,后面又攒了一些。大部分还是动效和布局:改一点,另一个地方就出问题;有几个方案折腾了好几轮,最后干脆不用了。
这里接着记十个,都是当时开发过程里的情况。
1. 本地正常,上线白屏
CSP 先拦了 Next.js 的内联脚本,页面白了;处理完脚本,Google Fonts 的 CSS 又被拦,中文字体没加载出来。
开发模式没碰到,生产构建才发现。所以改这类配置,要跑一次 npm run build 和 npm start,再看控制台具体拦了什么。字体现在已经改成本地托管,当时那套 Google Fonts 配置不用了。
2. setTimeout 也没解决水合错误
之前那篇里写 setTimeout(0) 能解决,后来发现还是会报错。
接着试了 50ms、配合 rAF、两次 rAF、requestIdleCallback,依然不稳定。最后把动画锁从 data-locked 属性移到 JS 的 Map 里,不再往 DOM 上写这个状态。
动画要用的 class 仍然会改,只是内部计时锁没必要挂在元素上。继续调延迟没有解决这个问题。
3. 首页项目横向滚动,最后放弃了
原生 overflow-x、把滚轮转成横向滚动、RAF 缓冲、GSAP pin 和不 pin,前后换了六轮,体验都不太满意。
最后就是一行项目名称、图标和标签,放不下就换行。首页只是想让人看见我做了哪些项目,没必要为这几个入口再折腾滚轮。
4. 十二层等高线,一坨绿
Spotlight 想做成地形等高线,叠了十二层 SVG path。全填色就一坨绿,改描边又看不清,用 brightness 和 opacity 做呼吸,颜色还是灰灰脏脏的。
最后回到一个有机泡泡,和 Hero 用同一套形状。十二层那个版本就放弃了。
5. 入场动画把 hover 覆盖了
.reveal 里用 transition 简写,列了 opacity、transform、filter、clip-path,没列背景色和文字颜色。其他地方写的 hover 过渡就被覆盖了。
试过长属性和 !important,最后还是回到简写,把 background-color 130ms ease 0s 和 color 130ms ease 0s 也加进去。入场用 1.5s,hover 用 130ms,各自生效。
6. 点击涟漪,调了九次延迟
想在点击链接的位置扩散一个绿圆,再跳转页面。延迟从 160ms 改到 220ms,又试了其他值,连 animationend 都用过,还是不对。
因为当时监听绑在 document 的冒泡阶段。事件到这里之前,React 那边已经处理了导航,再 preventDefault() 就晚了。
改成 document 捕获阶段监听后才拦到。要看的是监听位置和事件顺序,继续改动画时间没用。修饰键点击、新标签页这些原有操作也要单独检查。
7. 每个泡泡不用单独写 keyframes
几个泡泡共用一套动画,旋转和缩放完全一样,看起来很机械。给每个泡泡生成一份 keyframes 又太麻烦。
后来把参数换成 --s1、--r1、--o1 这些 CSS 变量,每个元素传自己的缩放、角度和透明度,共用动画就行。TypeScript 里给这些变量补上类型。
8. 说好随机,怎么大小还有规律
看着像泡泡从上到下越来越小。检查后发现,左边和右边用的大小范围不同,排列又是左右交替的。
把 size 和 side 分开,统一尺寸范围就好了。随机数没问题,是后面自己加的条件让它看起来有规律。
9. 泡泡变大,挤进正文了
把尺寸从 160px 放大到 340px,中心位置没跟着改,边缘就伸进正文区域。
后来把横向位置限制在侧边距外侧的一部分。大小和位置要一起调,窄屏也得看,不能只在大屏上把它放大就算完了。
10. 页面尺寸变一下,泡泡又生成一次
泡泡数量跟页面高度走,ResizeObserver 触发后更新数量,渲染时又会生成一遍数组,可能闪烁或者重挂载。
那次用 useMemo 按 slug 和 count 缓存生成结果,减少无关渲染时的重算。count 真变了还是会重新生成,所以 key 是否稳定、随机结果能不能复现,也要另外看。