嘿,各位!今天咱们来聊聊JS Web Animations API
(WAAPI) 和 CSS 动画这对好基友,看看它们是如何互动的,以及性能方面的一些门道。别担心,我会尽量用大白话,加上一些实际的代码示例,让大家更容易理解。
开场白:动画世界的两大门派
在前端动画的世界里,CSS 动画和 Web Animations API 就像是两大门派,各有千秋,也各有拥趸。CSS 动画简单易用,声明式风格,而 WAAPI 则更加灵活,可以通过 JavaScript 精细控制。两者并非水火不容,而是可以互相补充,共同构建更炫酷的动画效果。
第一部分:WAAPI 基础入门
首先,咱们来快速回顾一下 WAAPI 的基本用法。WAAPI 允许你通过 JavaScript 创建和控制动画。
-
创建动画:
element.animate()
这是 WAAPI 的核心方法,用于创建一个新的动画。它接受两个参数:
keyframes
: 一个描述动画关键帧的数组或对象。options
: 一个包含动画选项的对象,例如duration
(持续时间)、easing
(缓动函数)等。
const element = document.getElementById('myElement'); const animation = element.animate( [ { transform: 'translateX(0)' }, { transform: 'translateX(100px)' } ], { duration: 1000, // 动画持续 1 秒 easing: 'ease-in-out' // 缓动函数 } );
-
控制动画:
Animation
对象element.animate()
方法返回一个Animation
对象,你可以用它来控制动画的播放、暂停、反转等。animation.play(); // 播放动画 animation.pause(); // 暂停动画 animation.reverse(); // 反转动画 animation.cancel(); // 取消动画 animation.finish(); // 立即完成动画
-
timeline
: 动画时间线, 默认document.timeline
const myTimeline = new AnimationTimeline(); const animation = element.animate( [ { transform: 'translateX(0)' }, { transform: 'translateX(100px)' } ], { duration: 1000, timeline: myTimeline } ); myTimeline.play(animation);
第二部分:WAAPI 与 CSS 动画的互操作性
现在,咱们来聊聊 WAAPI 如何与 CSS 动画协同工作。
-
读取 CSS 动画属性
WAAPI 可以读取 CSS 动画的属性,例如
animation-duration
、animation-timing-function
等。这使得你可以根据 CSS 中定义的动画属性来动态创建 WAAPI 动画。const element = document.getElementById('myElement'); // 获取 CSS 动画的持续时间 const duration = parseFloat(getComputedStyle(element).animationDuration) * 1000; // 转换为毫秒 // 创建一个与 CSS 动画持续时间相同的 WAAPI 动画 const animation = element.animate( [ { transform: 'translateX(0)' }, { transform: 'translateX(100px)' } ], { duration: duration, easing: 'ease-in-out' } );
-
覆盖 CSS 动画
WAAPI 可以覆盖 CSS 动画。当使用 WAAPI 创建的动画与 CSS 动画作用于相同的元素和属性时,WAAPI 动画会优先执行。
<style> #myElement { animation: move 2s linear infinite; } @keyframes move { from { transform: translateX(0); } to { transform: translateX(100px); } } </style> <div id="myElement">Hello</div> <script> const element = document.getElementById('myElement'); // 使用 WAAPI 覆盖 CSS 动画 const animation = element.animate( [ { transform: 'translateX(0)' }, { transform: 'translateX(200px)' } // 修改终点位置 ], { duration: 1000, easing: 'ease-in-out' } ); </script>
在这个例子中,虽然 CSS 动画定义了
translateX(100px)
的终点位置,但 WAAPI 动画将其覆盖为translateX(200px)
。 -
组合 CSS 动画和 WAAPI 动画
你可以将 CSS 动画和 WAAPI 动画组合起来,实现更复杂的动画效果。例如,你可以使用 CSS 动画来定义一个基础的动画,然后使用 WAAPI 来添加一些交互效果。
<style> #myElement { animation: rotate 5s linear infinite; } @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } </style> <div id="myElement">Hello</div> <script> const element = document.getElementById('myElement'); element.addEventListener('mouseover', () => { // 当鼠标悬停时,使用 WAAPI 暂停 CSS 动画 element.getAnimations().forEach(animation => animation.pause()); }); element.addEventListener('mouseout', () => { // 当鼠标离开时,使用 WAAPI 恢复 CSS 动画 element.getAnimations().forEach(animation => animation.play()); }); </script>
在这个例子中,CSS 动画定义了一个旋转动画,当鼠标悬停在元素上时,WAAPI 暂停动画,鼠标离开时,WAAPI 恢复动画。
-
使用 CSS 自定义属性 (CSS Variables) 与 WAAPI
CSS 自定义属性可以作为桥梁,让 CSS 动画和 WAAPI 动画共享一些动画参数。
<style> #myElement { --translateX: 0; transform: translateX(var(--translateX)); transition: transform 0.5s ease-in-out; /* 使用 CSS 过渡 */ } </style> <div id="myElement">Hello</div> <script> const element = document.getElementById('myElement'); // 使用 WAAPI 修改 CSS 自定义属性的值 element.animate( [ { '--translateX': '0px' }, { '--translateX': '100px' } ], { duration: 500, easing: 'ease-in-out', fill: 'forwards' // 保持动画结束时的状态 } ); </script>
在这个例子中,我们使用了 CSS 自定义属性
--translateX
来控制元素的水平位置。 WAAPI 动画修改了这个属性的值,从而触发了 CSS 过渡效果。
第三部分:WAAPI 的性能优化
现在,咱们来深入探讨一下 WAAPI 的性能优化。虽然 WAAPI 提供了更多的灵活性,但如果不注意优化,可能会导致性能问题。
-
硬件加速
WAAPI 动画默认情况下会尝试使用硬件加速。但是,某些 CSS 属性可能会阻止硬件加速,例如
box-shadow
、filter
等。为了确保动画能够获得硬件加速,建议使用
transform
和opacity
属性进行动画。这些属性通常可以由 GPU 直接处理,从而提高性能。 -
避免频繁的布局和重绘
频繁的布局和重绘是导致动画性能问题的常见原因。当修改元素的样式时,浏览器需要重新计算元素的布局,并重新绘制屏幕。这会消耗大量的计算资源。
为了避免频繁的布局和重绘,建议使用
requestAnimationFrame()
方法来更新动画。requestAnimationFrame()
方法会在浏览器下一次重绘之前调用指定的函数,从而确保动画的更新与浏览器的刷新同步。const element = document.getElementById('myElement'); let x = 0; function animate() { x += 1; element.style.transform = `translateX(${x}px)`; requestAnimationFrame(animate); } requestAnimationFrame(animate);
-
使用
will-change
属性will-change
属性可以告诉浏览器,元素将要发生哪些变化。这可以让浏览器提前进行优化,从而提高动画性能。例如,如果你知道元素将要进行
transform
动画,可以使用以下代码:#myElement { will-change: transform; }
-
减少动画元素的数量
动画元素的数量越多,浏览器需要处理的计算量就越大。因此,建议尽量减少动画元素的数量。
如果需要对大量的元素进行动画,可以考虑使用 Canvas 或 WebGL。这些技术可以更高效地处理大量的图形。
-
使用合成层 (Compositing Layers)
浏览器会把网页分成多个层,每个层都可以独立进行绘制和动画。 把需要进行动画的元素放到一个独立的合成层中,可以避免动画影响到其他元素,提高性能。
你可以通过 CSS 属性
transform: translateZ(0);
或者backface-visibility: hidden;
来强制创建一个新的合成层。 注意不要滥用,创建过多的合成层也会消耗资源。 -
优化
keyframes
keyframes
数组中关键帧的数量也会影响性能。过多的关键帧会增加计算量。 尽量减少关键帧的数量,或者使用更简单的缓动函数,可以提高性能。
第四部分:WAAPI 的高级技巧
除了基本用法和性能优化之外,WAAPI 还有一些高级技巧,可以帮助你创建更复杂的动画效果。
-
使用
AnimationEffectTimingProperties
AnimationEffectTimingProperties
接口提供了一些用于控制动画时间线的属性,例如delay
(延迟)、iterations
(迭代次数)、direction
(方向)等。const element = document.getElementById('myElement'); const animation = element.animate( [ { transform: 'translateX(0)' }, { transform: 'translateX(100px)' } ], { duration: 1000, easing: 'ease-in-out', delay: 500, // 动画延迟 500 毫秒 iterations: 2, // 动画重复 2 次 direction: 'alternate' // 动画在每次迭代时反向播放 } );
-
使用
AnimationTimeline
AnimationTimeline
接口代表一个动画时间线。你可以将多个动画添加到同一个时间线上,从而控制它们的播放顺序和时间。const element1 = document.getElementById('element1'); const element2 = document.getElementById('element2'); const timeline = document.timeline; // 获取默认的文档时间线 const animation1 = element1.animate( [ { transform: 'translateX(0)' }, { transform: 'translateX(100px)' } ], { duration: 1000, easing: 'ease-in-out' } ); const animation2 = element2.animate( [ { transform: 'translateY(0)' }, { transform: 'translateY(100px)' } ], { duration: 1000, easing: 'ease-in-out', delay: 500 // 延迟 500 毫秒开始 } ); // animation1 和 animation2 会先后播放
-
监听动画事件
WAAPI 提供了
onfinish
和oncancel
事件,用于监听动画的完成和取消。const element = document.getElementById('myElement'); const animation = element.animate( [ { transform: 'translateX(0)' }, { transform: 'translateX(100px)' } ], { duration: 1000, easing: 'ease-in-out' } ); animation.onfinish = () => { console.log('动画完成了!'); }; animation.oncancel = () => { console.log('动画被取消了!'); };
-
使用
CustomEffect
CustomEffect
接口允许你创建自定义的动画效果。 你可以使用 JavaScript 来计算动画的每一帧,从而实现更复杂的动画效果。 这通常用于更高级的动画场景,比如粒子效果。
第五部分:WAAPI 与 CSS 动画的对比
为了更好地理解 WAAPI 和 CSS 动画的优缺点,咱们来做一个简单的对比:
特性 | Web Animations API (WAAPI) | CSS 动画 |
---|---|---|
控制方式 | JavaScript | CSS |
灵活性 | 非常高 | 相对较低 |
性能 | 需要优化,注意硬件加速 | 优化较好,通常硬件加速 |
易用性 | 相对复杂 | 简单易用 |
适用场景 | 复杂的交互动画,动态动画 | 简单的过渡效果,状态切换动画 |
与 JS 的集成度 | 高 | 较低 |
事件支持 | 提供 onfinish , oncancel |
animationstart , animationend , animationiteration |
结论:选择合适的工具
WAAPI 和 CSS 动画各有优缺点。选择哪种技术取决于你的具体需求。
- 如果你需要创建简单的过渡效果或状态切换动画,CSS 动画通常是更好的选择。
- 如果你需要创建复杂的交互动画或动态动画,WAAPI 提供了更多的灵活性。
- 在性能方面,两者都需要注意优化。WAAPI 需要确保硬件加速,避免频繁的布局和重绘。CSS 动画也需要避免使用会导致性能问题的属性。
最后:代码示例(一个综合的例子)
<!DOCTYPE html>
<html>
<head>
<title>WAAPI and CSS Animation Example</title>
<style>
#box {
width: 100px;
height: 100px;
background-color: red;
position: relative; /* 必须设置,才能使用 transform */
will-change: transform; /* 告诉浏览器,我们将要对 transform 属性进行动画 */
/* 使用 CSS 动画作为基础 */
animation: colorChange 5s linear infinite;
}
@keyframes colorChange {
0% { background-color: red; }
50% { background-color: blue; }
100% { background-color: red; }
}
</style>
</head>
<body>
<div id="box"></div>
<script>
const box = document.getElementById('box');
// 当鼠标悬停时,使用 WAAPI 改变 box 的位置
box.addEventListener('mouseover', () => {
const translateX = Math.random() * 200 + 'px'; // 随机水平偏移量
const translateY = Math.random() * 100 + 'px'; // 随机垂直偏移量
// 使用 WAAPI 创建一个动画
const moveAnimation = box.animate(
[
{ transform: 'translate(0, 0)' },
{ transform: `translate(${translateX}, ${translateY})` }
],
{
duration: 500,
easing: 'ease-out',
fill: 'forwards' // 保持动画结束的状态
}
);
});
// 当鼠标离开时,重置 box 的位置
box.addEventListener('mouseout', () => {
const resetAnimation = box.animate(
[
{ transform: getComputedStyle(box).transform }, // 获取当前 transform
{ transform: 'translate(0, 0)' }
],
{
duration: 300,
easing: 'ease-in',
fill: 'forwards'
}
);
});
// 监听动画事件
box.getAnimations().forEach(animation => {
animation.onfinish = () => {
console.log('动画完成!');
};
});
</script>
</body>
</html>
这个例子结合了 CSS 动画(颜色变化)和 WAAPI 动画(位置变化),展示了两者如何协同工作。
好了,今天的分享就到这里。希望大家有所收获!记住,动画的世界是充满乐趣的,多尝试,多实践,你也能成为动画大师!