0

0

如何通过JavaScript的Element.animate实现原生动画,以及它对比CSS动画的控制灵活性有哪些?

夜晨

夜晨

发布时间:2025-09-22 09:40:01

|

1028人浏览过

|

来源于php中文网

原创

Element.animate结合了CSS动画的性能优势与JavaScript的灵活控制,适合需要交互和动态调整的复杂动画场景。

如何通过javascript的element.animate实现原生动画,以及它对比css动画的控制灵活性有哪些?

Element.animate
提供了一种非常强大的方式,让我们能用 JavaScript 直接控制动画,它本质上是 Web Animations API 的核心,将 CSS 动画的声明式优势与 JavaScript 的命令式控制能力巧妙地结合了起来。说实话,它给我的感觉就像是,你既能享受浏览器底层优化的性能红利,又能像操作普通 JavaScript 对象一样,随心所欲地控制动画的播放、暂停、反转,甚至在时间轴上“拖拽”动画进度。相较于纯 CSS 动画,它的控制灵活性简直是质的飞跃,尤其是在处理那些需要与用户交互、动态生成或复杂序列的动画时,这种优势体现得淋漓尽致。

解决方案

要使用

Element.animate
,核心就是调用元素上的
animate()
方法。这个方法接受两个参数:
keyframes
options

keyframes
定义了动画的关键帧,你可以把它想象成动画在不同时间点应该呈现的状态。它既可以是一个数组,每个元素是一个表示帧状态的对象,也可以是一个包含所有属性的对象,浏览器会根据属性值数组自动生成帧。

// 示例1: 数组形式的关键帧
const element = document.getElementById('myElement');
element.animate(
  [
    { transform: 'translateX(0)', opacity: 1 }, // 0% 状态
    { transform: 'translateX(100px)', opacity: 0.5 }, // 50% 状态
    { transform: 'translateX(200px)', opacity: 1 } // 100% 状态
  ],
  {
    duration: 1000, // 动画持续时间1秒
    easing: 'ease-in-out', // 缓动函数
    fill: 'forwards', // 动画结束后保持最终状态
    iterations: Infinity // 无限循环
  }
);

// 示例2: 对象形式的关键帧 (更适合简单的 from/to 动画或多个属性)
element.animate(
  {
    transform: ['scale(1)', 'scale(1.2)', 'scale(1)'], // 从1到1.2再到1
    backgroundColor: ['red', 'blue', 'red']
  },
  {
    duration: 1500,
    easing: 'linear',
    iterations: 2 // 播放两次
  }
);

options
是一个对象,用于配置动画的各种行为,比如持续时间 (
duration
)、缓动函数 (
easing
)、延迟 (
delay
)、填充模式 (
fill
)、循环次数 (
iterations
) 等。这些参数与 CSS
@keyframes
规则中的属性非常相似,但在这里它们被封装成了一个 JavaScript 对象,方便我们动态调整。

立即学习Java免费学习笔记(深入)”;

animate()
方法会返回一个
Animation
对象,这才是真正赋予我们强大控制力的关键。通过这个对象,你可以:

  • play()
    : 播放动画。
  • pause()
    : 暂停动画。
  • reverse()
    : 反转动画播放方向。
  • finish()
    : 立即跳到动画结束状态。
  • cancel()
    : 立即停止动画并移除所有动画效果,回到初始状态。
  • commitStyles()
    : 将动画的最终状态应用为元素的实际样式,这在
    fill: 'forwards'
    的情况下尤其有用,它能确保动画结束后样式真正“固定”下来。
  • 监听事件:
    animation.onfinish
    animation.oncancel
    可以让你在动画完成或取消时执行回调函数
const myAnimation = element.animate(
  [{ opacity: 0 }, { opacity: 1 }],
  { duration: 500 }
);

// 动画播放完成后执行
myAnimation.onfinish = () => {
  console.log('动画播放完毕!');
  // 此时可以执行一些后续操作,比如移除元素或触发下一个动画
  myAnimation.commitStyles(); // 将最终样式应用到元素上
  myAnimation.cancel(); // 确保动画对象不再活跃,但样式已固定
};

// 按钮点击暂停
document.getElementById('pauseButton').addEventListener('click', () => {
  myAnimation.pause();
});

// 按钮点击反转
document.getElementById('reverseButton').addEventListener('click', () => {
  myAnimation.reverse();
});

这种基于

Animation
对象的控制,让我觉得它比纯粹的 CSS 动画更像是一个“活”的实体,你可以随时与它互动,而不是仅仅声明一次就放任自流。

Element.animate与CSS动画:何时选择何者以优化性能和开发效率?

选择

Element.animate
还是 CSS 动画,这其实是个挺有意思的问题,没有绝对的答案,更多的是看你的具体场景和需求。我个人在做决策时,通常会从几个维度去考量。

首先,对于那些简单的、声明式的、不需要太多交互的 UI 动画,比如按钮的

:hover
效果、模态框的进出场、加载指示器等等,我几乎总是倾向于使用纯 CSS 动画。原因很简单,CSS 动画通常能被浏览器优化得非常好,它们可以运行在独立的合成器线程上,这意味着即使主线程被 JavaScript 任务阻塞,动画也能保持流畅。这对于提升用户体验至关重要,特别是那些“消防即忘”的动画,写起来也快,维护起来也清晰。

然而,一旦动画需求变得复杂,需要动态计算、用户交互驱动、或者涉及多个动画的编排和同步时,CSS 动画的局限性就显现出来了。比如,一个拖拽手势驱动的元素移动,或者一个根据用户滚动位置改变速度和方向的视差效果,再或者一个需要精确暂停、快进、倒退的自定义播放器进度条动画,这些场景下,

Element.animate
就成了我的首选。

Element.animate
的优势在于它提供了 JavaScript 的编程能力。你可以动态地修改动画的持续时间、缓动函数、关键帧,甚至在动画播放过程中改变其
playbackRate
currentTime
。这种细粒度的控制是 CSS 动画难以企及的。想象一下,你想做一个动画,它根据用户的输入速度来调整播放速度,或者在某个特定事件发生时,立即跳到动画的某个百分比,这些在
Element.animate
里实现起来简直是如鱼得水,而用 CSS 动画则会非常痛苦,甚至无法实现。

性能方面,虽然很多人觉得 JS 动画不如 CSS 动画,但

Element.animate
实际上是 Web Animations API 的一部分,它在设计之初就考虑到了性能。对于许多可合成的属性(如
transform
opacity
),浏览器同样可以将其优化到合成器线程上运行,与 CSS 动画享有类似的性能优势。所以,并非所有 JS 动画都慢,关键在于你动画的属性以及浏览器的实现。

总结一下,如果动画是静态的、简单的、无交互的,并且性能是首要考虑,那就用 CSS 动画。如果动画是动态的、复杂的、需要精细控制和交互的,那么

Element.animate
绝对是更高效、更灵活的选择。它提供了一个完美的平衡点,既能利用浏览器底层的优化,又能享受 JavaScript 带来的强大控制力。

如何利用Element.animate的Animation对象实现动画的精细化控制与交互?

Element.animate
返回的
Animation
对象,我把它看作是动画的“遥控器”,它上面挂载了一系列属性和方法,让我们能对动画进行前所未有的精细化控制。这正是
Element.animate
区别于 CSS 动画,并使其在交互式动画中大放异彩的关键。

Red Panda AI
Red Panda AI

AI文本生成图像

下载

最直观的控制当然是

play()
pause()
reverse()
这些方法。但真正深入的控制,需要关注
currentTime
playbackRate
这两个属性。

currentTime
属性允许你获取或设置动画当前的时间位置(以毫秒为单位)。这意味着你可以像操作视频播放器一样,精确地将动画“拖拽”到任何时间点。这在实现动画的“scrubbing”(擦洗,即通过拖动滑块来控制动画进度)功能时非常有用。

const animation = element.animate(
  [{ transform: 'translateX(0px)' }, { transform: 'translateX(200px)' }],
  { duration: 2000, fill: 'forwards' }
);
animation.pause(); // 先暂停,等待用户控制

const slider = document.getElementById('animationSlider');
slider.max = animation.duration; // 滑块最大值设为动画时长

slider.addEventListener('input', (event) => {
  animation.currentTime = event.target.value; // 根据滑块值设置动画当前时间
});

// 你甚至可以根据用户滚动事件来改变 currentTime,实现滚动视差动画
window.addEventListener('scroll', () => {
  const scrollProgress = window.scrollY / (document.body.scrollHeight - window.innerHeight);
  animation.currentTime = animation.duration * scrollProgress;
});

通过

currentTime
,你可以将动画的播放进度与任何外部事件(如鼠标拖动、滚动条位置、音频播放进度)关联起来,创造出非常丰富的交互体验。

另一个强大的属性是

playbackRate
,它控制着动画的播放速度。默认值是
1
,表示正常速度。设置为
2
会让动画快进一倍,设置为
0.5
则会慢放一倍。设置为负值(例如
-1
)则会以正常速度反向播放。这对于实现一些动态效果非常有用,比如根据用户操作的紧急程度来加速或减速动画。

// 鼠标进入元素时加速动画,移出时恢复正常
element.addEventListener('mouseenter', () => {
  animation.playbackRate = 2; // 加速
  animation.play(); // 确保正在播放
});

element.addEventListener('mouseleave', () => {
  animation.playbackRate = 1; // 恢复正常速度
});

除了这些,

Animation
对象还提供了几个 Promise 属性,用于处理动画的生命周期:

  • animation.ready
    : 这是一个 Promise,当动画准备好播放时(即动画的
    delay
    结束,或者动画从暂停状态恢复时),它会解析。这对于确保动画在特定时机开始执行后续操作很有用。
  • animation.finished
    : 这是一个 Promise,当动画完成播放时(包括
    fill: 'forwards'
    状态),它会解析。它比
    onfinish
    事件更适合链式调用和异步处理。
animation.finished.then(() => {
  console.log('动画已经完全结束并固定了最终状态。');
  // 可以在这里触发下一个动画,或者执行一些清理工作
}).catch(() => {
  console.log('动画被取消了。');
});

这些属性和方法共同构成了

Animation
对象的强大能力,让开发者能够以编程的方式,对动画进行极致的精细化控制,从而创造出高度动态和交互式的 Web 体验。

Element.animate在复杂场景下的挑战与兼容性考量:你可能遇到的坑点?

尽管

Element.animate
功能强大,但在实际项目,尤其是在复杂场景下,它并非没有挑战和一些需要注意的坑点。我自己在实践中就遇到过一些,这里总结一下,希望能给大家一些前车之鉴。

首先,浏览器兼容性是老生常谈的问题。虽然现代浏览器对 Web Animations API 的支持已经相当不错,但如果你需要支持一些老旧的浏览器(比如 IE 或者某些版本的 Safari),那么

Element.animate
可能需要一个 Polyfill。
web-animations-js
就是一个非常流行的 Polyfill 库,它可以让 Web Animations API 在不支持的浏览器中也能正常工作。不过,引入 Polyfill 意味着增加了文件大小和潜在的性能开销,所以在项目初期就需要评估目标用户群体的浏览器分布。

// 如果需要兼容性,可能需要引入 polyfill
// import 'web-animations-js'; // 或通过 CDN 引入

其次,关于

fill
模式和
commitStyles()
fill: 'forwards'
是一个非常方便的选项,它让动画结束后元素保持最终状态。但这里有个小陷阱:
fill: 'forwards'
只是让元素看起来停留在最终状态,它并没有真正改变元素的内联样式或计算样式。这意味着,如果你在动画结束后尝试获取元素的样式,或者在后续的 CSS 规则中覆盖了动画的最终状态,可能会出现意想不到的结果。
commitStyles()
方法就是用来解决这个问题的,它会将动画的最终状态作为元素的实际样式应用上去。我个人建议,如果你希望动画结束后状态是持久的,并且不希望它被轻易覆盖,那么在
onfinish
回调中调用
commitStyles()
是一个好习惯。

const anim = element.animate(
  [{ transform: 'translateX(0)' }, { transform: 'translateX(100px)' }],
  { duration: 1000, fill: 'forwards' }
);

anim.onfinish = () => {
  anim.commitStyles(); // 确保 translateX(100px) 成为元素的实际样式
  anim.cancel(); // 此时动画对象可以安全地被取消,因为样式已固定
};

再来是 性能考量,虽然我前面提到

Element.animate
可以享受浏览器优化,但并非所有属性的动画都能被合成器线程处理。只有像
transform
(包括
translate
,
scale
,
rotate
) 和
opacity
这些属性,浏览器才能高效地将其 offload 到 GPU。如果你动画的属性是
width
height
top
left
background-color
等等,它们很可能仍然会在主线程上执行,这就有可能导致动画卡顿,尤其是在主线程繁忙时。所以在设计动画时,尽量优先使用可合成的属性,这是优化性能的关键。

最后,与 CSS

transition
/
Animation
的交互
。当一个元素同时被 CSS 动画或过渡和
Element.animate
影响时,
Element.animate
的优先级通常更高。这意味着 JavaScript 动画可能会覆盖或干扰已有的 CSS 动画。这既是优点(可以动态控制),也可能是坑(如果不小心可能会导致样式冲突或预期外的行为)。在开发时,最好明确哪些动画由 CSS 负责,哪些由
Element.animate
控制,避免不必要的重叠和冲突。

这些挑战并非不可逾越,但了解它们能帮助我们更好地规划和实现动画,避免在项目后期才发现问题,从而提升开发效率和动画的稳定性。

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

541

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

372

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

727

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

470

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

391

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

990

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

653

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

543

2023.09.20

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

0

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 0.7万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 2.7万人学习

CSS教程
CSS教程

共754课时 | 17.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号