如何使用JavaScript获取文档时间轴中的动画结束值及多CSS动画总运行时长
我来帮你拆解这两个问题,结合你提到的document.getAnimations()方法,给你梳理最准确高效的实现方案:
获取Chrome DevTools动画面板显示的总运行时长
Chrome DevTools里的动画总时长,本质是从页面中第一个动画开始,到最后一个动画结束的时间跨度。用document.getAnimations()可以精准获取所有活跃的(包括动态添加类触发的)CSS动画和Web Animations API动画,后续只需要计算时间范围即可:
实现代码
function calculateTotalAnimationDuration() { const allAnimations = document.getAnimations(); if (allAnimations.length === 0) return 0; // 遍历所有动画,计算每个动画的实际开始/结束时间 const animationTimings = allAnimations.map(anim => { const computedTiming = anim.effect.getComputedTiming(); // 动画实际开始时间(已考虑delay) const startTime = anim.startTime; // 动画总持续时间:单次时长 × 迭代次数(包含重复播放的时间) const totalDuration = computedTiming.duration * computedTiming.iterationCount; // 动画实际结束时间 const endTime = startTime + totalDuration; return { startTime, endTime }; }); // 找到最早的开始时间和最晚的结束时间 const earliestStart = Math.min(...animationTimings.map(t => t.startTime)); const latestEnd = Math.max(...animationTimings.map(t => t.endTime)); // 返回总时长(毫秒为单位) return latestEnd - earliestStart; } // 使用示例 console.log("总动画时长:", calculateTotalAnimationDuration(), "ms");
准确性与效率分析
- 准确性:完全依赖浏览器提供的动画原生数据,能精准捕获动态添加类触发的动画,包括迭代次数、延迟等细节,和DevTools面板显示的时长完全一致。
- 效率:时间复杂度为O(n)(n为页面动画数量),仅遍历一次动画数组并计算极值,性能开销可以忽略不计。
获取动画结束值
动画结束值分为两种场景:动画结束后元素的实际样式值,和动画定义的最终关键帧值,下面分别给出对应方案:
场景1:获取动画结束后元素的实际样式值(fill: forwards时适用)
如果你的CSS动画设置了animation-fill-mode: forwards,动画结束后元素会保持最终状态,直接用getComputedStyle就能拿到准确值,还能等待动画结束后异步获取:
async function getFinalElementStyle(element, cssProperty) { const elementAnimations = element.getAnimations(); if (elementAnimations.length === 0) { return getComputedStyle(element)[cssProperty]; } // 等待所有动画完成 await Promise.all(elementAnimations.map(anim => anim.finished)); // 返回最终计算样式 return getComputedStyle(element)[cssProperty]; } // 使用示例 getFinalElementStyle(document.querySelector(".animated-box"), "opacity") .then(endValue => console.log("最终透明度:", endValue));
分析
- 准确性:仅当
fill: forwards时准确,直接反映元素的真实状态。 - 效率:异步执行不阻塞主线程,等待动画结束的过程无额外性能开销。
场景2:获取动画定义的最终关键帧值(不受fill模式影响)
如果动画没有设置fill: forwards,或者你需要获取动画本身定义的结束值(不管元素最终状态),可以直接解析动画的关键帧:
通用实现(兼容CSS动画和Web Animations)
function getAnimationKeyframeEndValue(element, cssProperty) { const elementAnimations = element.getAnimations(); // 先处理Web Animations API创建的动画 for (const anim of elementAnimations) { if (anim.effect instanceof KeyframeEffect) { const keyframes = anim.effect.getKeyframes(); if (keyframes.length > 0) { const lastKeyframe = keyframes[keyframes.length - 1]; if (lastKeyframe[cssProperty]) return lastKeyframe[cssProperty]; } } } // 再处理CSS动画(解析@keyframes规则) const computedStyle = getComputedStyle(element); const animationNames = computedStyle.animationName.split(", ").filter(name => name !== "none"); for (const name of animationNames) { // 遍历所有样式表,找到对应的@keyframes规则 const keyframeRules = Array.from(document.styleSheets).flatMap(sheet => { try { return Array.from(sheet.cssRules).filter(rule => rule.type === CSSRule.KEYFRAMES_RULE && rule.name === name ); } catch (e) { // 跨域样式表无法访问,直接跳过 return []; } }); if (keyframeRules.length > 0) { // 找到最后一个关键帧(兼容100%和to关键字) const lastKeyframe = [...keyframeRules[0].cssRules].sort((a, b) => { const aPercent = a.keyText === "to" ? 100 : parseFloat(a.keyText); const bPercent = b.keyText === "to" ? 100 : parseFloat(b.keyText); return bPercent - aPercent; })[0]; if (lastKeyframe.style[cssProperty]) return lastKeyframe.style[cssProperty]; } } // 如果没找到动画,返回当前计算样式 return getComputedStyle(element)[cssProperty]; } // 使用示例 console.log("动画定义的结束透明度:", getAnimationKeyframeEndValue(document.querySelector(".animated-box"), "opacity"));
分析
- 准确性:不受
fill模式影响,能直接获取动画定义的最终值;唯一限制是跨域的CSS样式表无法访问@keyframes规则,这种情况会退回到当前计算样式。 - 效率:需要遍历样式表和关键帧,时间复杂度略高于场景1,但对于常规页面来说性能依然出色。
方案选择总结
- 总时长计算:优先使用
document.getAnimations()结合时间范围计算的方案,兼顾准确性和效率。 - 结束值获取:
- 若动画设置
fill: forwards,用场景1的异步获取计算样式方案,简单高效。 - 若需要动画定义的原始结束值,用场景2的关键帧解析方案,准确性更高。
- 若动画设置
内容的提问来源于stack exchange,提问作者Temp Dragon




