Astro 使用 View Transitions 后 AOS 动画只播放一次怎么解决
解决 Astro 启用 ClientRouter 后 AOS 滚动动画只在首次加载时播放,页面切换后不再触发的问题
解决 Astro 启用 ClientRouter 后 AOS 滚动动画只在首次加载时播放,页面切换后不再触发的问题
在 Astro 博客中启用 <ClientRouter /> (View Transitions) 后,你可能会发现 AOS (Animate On Scroll) 动画只在第一次打开网页时播放:
AOS 库的初始化配置中通常会设置 once: true,这意味着每个元素的动画只会触发一次:
AOS.init({
once: true, // 动画只播放一次
duration: 700,
// ...
});
当使用 View Transitions 进行页面切换时:
refresh() 刷新aos-animate 类,标记为已播放once: true 设置,不再触发动画简单说:元素的”已动画”状态没有被正确重置。
关键是在页面切换时重置所有 AOS 元素的状态,然后重新初始化。
<script>
import AOS from 'aos';
// AOS 初始化配置对象
const aosConfig = {
easing: "ease-out-quart",
once: true,
offset: 60,
duration: 700,
delay: 0,
anchorPlacement: 'top-bottom',
};
// AOS 初始化函数
function initAOS() {
// 处理 Prose 内容中的媒体元素
enhanceProseMedia();
AOS.init(aosConfig);
}
// 重置所有 AOS 元素的动画状态
function resetAOSElements() {
const aosElements = document.querySelectorAll('[data-aos]');
aosElements.forEach((el) => {
el.classList.remove('aos-animate');
el.removeAttribute('data-aos-animated');
});
}
// AOS 重新初始化函数(用于页面切换后)
function reinitAOS() {
// 先重置所有元素状态
resetAOSElements();
// 重新初始化
initAOS();
}
// View Transitions 页面切换处理(只注册一次)
// 注意:astro:page-load 在初始页面加载和每次页面切换后都会触发
if (!(window as any)._aosListenerAdded) {
(window as any)._aosListenerAdded = true;
// 在页面切换前重置动画状态
document.addEventListener('astro:before-swap', resetAOSElements);
// 页面切换后重新初始化 AOS
document.addEventListener('astro:page-load', reinitAOS);
// 初始页面也需要立即初始化(因为事件监听器是新添加的)
reinitAOS();
}
</script>
⚠️ 重要更新:在之前的版本中,我们在脚本末尾直接调用了
initAOS()。这会导致一个性能问题:astro:page-load事件在页面首次加载时也会触发。如果你既直接调用了initAOS(),又监听了astro:page-load,那么 AOS 会被初始化两次。在性能较强的 PC 端可能感觉不明显,但在手机端,这会导致动画卡顿两次(元素先出现,然后闪烁一下再次播放动画)。
正确做法:移除直接调用的
initAOS(),统一使用astro:page-load事件来驱动初始化。这样无论是首次加载还是页面切换,都只会执行一次初始化逻辑。
| 时机 | 事件 | 操作 |
|---|---|---|
| 页面切换前 | astro:before-swap | 重置元素状态,移除 aos-animate 类 |
| 页面切换后 | astro:page-load | 重新初始化 AOS,触发新页面的动画 |
resetAOSElements()?AOS 通过以下方式判断元素是否已经播放过动画:
aos-animate 类:元素动画完成后会添加此类data-aos-animated 属性:标记元素已触发动画在页面切换前移除这些标记,AOS 就会认为这些是”新元素”,从而正确触发动画。
once: true?你可能会想:直接改成 once: false 不就行了?
虽然这样确实能让动画重新播放,但会带来副作用:
保持 once: true + 重置元素状态的方案更加优雅。
用户点击链接
↓
astro:before-swap 事件触发
↓
resetAOSElements() 重置动画状态
↓
页面 DOM 切换
↓
astro:page-load 事件触发
↓
reinitAOS() 重新初始化 AOS
↓
动画正常播放 ✅
使用 (window as any)._aosListenerAdded 标志确保事件监听器只注册一次。如果不这样做,每次脚本执行都会添加新的监听器,导致内存泄漏和重复执行。
如果使用 TypeScript,可能需要扩展 Window 类型:
declare global {
interface Window {
_aosListenerAdded?: boolean;
}
}
在 Astro + View Transitions 环境下使用 AOS:
astro:before-swap 重置元素的动画状态astro:page-load 重新初始化 AOSonce: true 配置,维持原有动画体验这样就能让 AOS 动画在每次页面切换后都正常播放了!
Astro 使用 View Transitions 后 AOS 动画只播放一次怎么解决
www.jsom.top/post/astro-使用-view-transitions-后-aos-动画只播放一次怎么解决
Comments