一、简介
CSS 制作 Web 动画有两种方式: 帧动画(Keyframe Animation)和过渡动画(Transition Animation)。在不同的业务场景中,我们应该选择不同的动画方式,通常来说:对于交互元素,会使用过渡动画,而对于连续的装饰性元素,则应该使用帧动画。
本文章将会仔细讲解帧动画的相关知识,让你对其有一个较为全面的了解。
二、帧动画
1、基本概念
帧动画允许你通过指定 CSS 属性在不同时间点上的行为来创建动画效果,这些时间点被称为关键帧,所以又被称为关键帧动画。帧动画需要通过@keyframes
来创建。
关键帧动画的主要思想是在多个不同时间点的CSS代码块之间进行插值计算,这个插值计算由浏览器自动完成的,它会根据@keyframes
中定义的多个代码块中的样式变化,自动创建样式变化的中间状态,从而样式能够平滑的变化。
一个帧动画主要由两个关键部分组成:创建帧动画和使用帧动画。
2、创建帧动画
动画名称:
在使用@keyframes
创建关键帧动画时,会在其后面跟随一个自定义标识符,表示当前动画的名称,该名称可在其所在作用域内通过animation-name
使用。但要注意的是:不要使用CSS的关键词来命名动画,例如auto
、width
等等,这可能会导致动画失效。
@keyframes 动画名称 {...}
/* 正确命名 */
@keyframes fadeOut {...}
/* 错误命名 */
@keyframes auto {...}
动画名称标识符是严格区分大小写的,fadeOut
与fadeout
是两个不同的名称。而且如果多个动画使用相同的名称,则通过动画名称调用动画时,将以定义顺序中的最后一个动画为准。
/* 严格区分大小写 */
@keyframes fadeOut {...}
/* 两者并不相同 */
@keyframes fadeout {...}
/* 当命名重复时 以最后定义的为准 */
@keyframes fadeOut {...}
动画关键帧:
@keyframes
内部没有时间的概念,它只有一个百分比完成的概念,n%
表示的是动画完成的百分比,可以在0%
~100%
之间的任何一个位置定义,一个百分比表示一个关键帧,可以定义任意数量的关键帧。浏览器会在关键帧之间进行插值计算,使帧与帧之间的样式平滑的进行变化。
@keyframes 动画名称 {
/* 动画关键帧 */
0% {
/* 样式 */
}
/* 。。。 */
100% {
/* 样式 */
}
}
除了使用n%
之外,还有两个关键字from
、to
可以使用,两者分别等同于0%
和100%
。
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
/* 等同于 */
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
在 @keyframes
规则中的关键帧顺序并不重要,因为浏览器将按照百分比值的顺序播放,而不是按照它们在代码中出现的先后顺序播放。而且如果定义的关键帧百分比出现了重复的情况,则在执行时,会将重复的关键帧百分比进行合并,如果存在重复的属性,则属性值以最后定义的为准。
@keyframes move {
0% {
background: red;
transform: translateX(0);
}
50% {
background: yellow;
transform: translateX(200px);
}
100% {
background: red;
transform: translateX(0);
}
50% {
transform: translateX(50px);
opacity: 0.5;
}
}
/* 等同于 */
@keyframes move {
0% {
background: red;
transform: translateX(0);
}
50% {
background: yellow;
transform: translateX(50px);
opacity: 0.5;
}
100% {
background: red;
transform: translateX(0);
}
}
如果没有指定 0%
/ from
关键帧,以及100%
/ to
关键帧,则浏览器会以动画绑定元素的初始样式为内容,构造 0%
/ from
关键帧和100%
/ to
关键帧。
.d {
width: 100px;
height: 100px;
background: red;
animation: move 2s infinite;
}
@keyframes move {
/* 未定义 0%和100% 关键帧 */
50% {
background: yellow;
transform: translateX(200px);
width: 200px;
}
}
/* 等同于 */
@keyframes move {
0% {
width: 100px;
height: 100px;
background: red;
}
50% {
background: yellow;
transform: translateX(200px);
width: 200px;
}
100% {
width: 100px;
height: 100px;
background: red;
}
}
关键帧中的!important
:
关键帧中的!important
将会被忽略,并不会抬高样式属性值的权重。因为在元素动画过程中,关键帧中声明的样式不在级联的上下文中,但本身的权重已经超过所有的普通权重样式,所以再无法通过!important
来提高权重。
但是动画所绑定的元素上的样式如果使用了!important
,则该样式不会被动画中定义的样式属性值所影响,因为该权重超过了动画样式的权重。
@keyframes move {
0% {
background: red;
transform: translateX(0);
}
50% {
background: yellow;
transform: translateX(50px);
}
100% {
background: red;
transform: translateX(0);
}
}
.d {
width: 100px;
height: 100px;
/* 该样式使用了important提高权重 不会被动画关键帧中的样式所影响 */
background: red !important;
animation: move 2s infinite;
}
关键帧中的CSS自定义属性:
CSS的自定义属性是指以--
开头的声明的变量,其可以通过var(--属性名)
的形式,将自定义属性的属性值在作用域范围内的任何一个属性上使用:
.d {
/* 定义自定义属性 */
--w: 20px;
/* 使用自定义属性 等同于 width: 20px; */
width: var(--w);
height: 20px;
background: red;
/* 使用自定义属性 等同于 border: 20px solid #ccc; */
border: var(--w) solid #ccc;
}
自定义属性可以在关键帧中使用,可以正常渲染,但是如果在关键帧中修改自定义属性的值,且元素样式中使用了该自定义属性,则浏览器无法对其进行插值计算,也就无法实现平滑的动画效果。主要原因为CSS 自定义属性的值对于浏览器来说相当于一个字符串,字符串的变化属于是不连贯的属性,无法进行插值计算。
@keyframes changeW {
50% {
--w: 40px;
}
}
.d {
--w: 20px;
width: var(--w);
height: 20px;
background: red;
border: var(--w) solid #ccc;
animation: changeW 2s infinite;
}
该问题可以通过CSS的@property
特性来解决,但该特性属于实验性技术,有浏览器兼容性问题。因此此处就不展开叙述了,更多具体可查看: @property。
关键帧合并:
如果@keyframes
中定义的多个关键帧存在样式属性重复,则可以将多个关键帧进行合并定义,减少代码重复。再将各个关键帧中不重复的样式属性进行单独定义,浏览器会将定义两次的关键帧内的样书进行合并,如果存在重复的属性,则属性值以最后定义的为准。
@keyframes move {
/* 将多个关键帧中的重复样式属性合并定义 */
0%,
100% {
background: red;
transform: translateX(0);
width: 100px;
}
50% {
background: yellow;
transform: translateX(200px);
width: 200px;
}
/* 不重复的样式属性再进行单独定义 */
100% {
/* 该关键帧的定义位于后面 所以属性值优先级高 */
width: 150px;
}
}
/* 等同于 */
@keyframes move {
0%{
background: red;
transform: translateX(0);
width: 100px;
}
50% {
background: yellow;
transform: translateX(200px);
width: 200px;
}
100% {
background: red;
transform: translateX(0);
width: 150px;
}
}
3、使用帧动画
@keyframes
仅仅是定义了在所在作用域内全局都可以使用的关键帧动画,定义好了动画的过程,但是其并不能自动应用。还需要通过CSS的animation
属性来显式的指定关键帧动画应用的元素,以及一系列相关的动画属性。
.test-div {
/* 使用动画 */
animation: fadeOut 500ms linear;
}
animation-name:
该属性用于指定引用@keyframs
动画的名称,必须严格遵守大小写,fadeOut
与fadeout
是两个不同的名称。
animation-name:fadeOut;
该属性可以设置多个动画名称,属性值之间通过,
进行分隔,表示同时引用多个动画。
animation-name:fadeOut, moveTo;
animation-duration:
该属性用于指定动画完成一个周期所需要的时间,值必须为非负数,单位为s
或ms
,默认为0。该时间定义了**@keyframes
** 中的的时间轴,关键帧百分比值(n%
)对应的时间节点是相对于该时间轴来计算的。
animation-duration: 2s;
animation-duration: 200ms;
如果该属性值为0,则动画效果是否可见取决于animation-fill-mode
属性的值,而animation-fill-mode
的表现效果又受到animaiton-direction
属性的影响。具体情况分为下面四种:
- 如果
animation-fill-mode: none;
,无论animaiton-direction
设置什么值,则动画无任何视觉效果。 - 如果
animation-fill-mode: forwards/both;
,且animaiton-direction: normal/alternate;
,则动画会直接显示最后一帧的样式效果。 - 如果
animation-fill-mode: forwards/both;
,且animaiton-direction: reverse/alternate-reverse;
,则动画会直接显示第一帧的样式效果。 - 如果
animation-fill-mode: backwards;
,无论animaiton-direction
设置什么值,则动画都会直接显示第一帧的样式效果。
该属性可以设置多个运动时间,属性值之间通过,
进行分隔,设置的多个属性值会按照顺序映射到对应的animation-name
中的动画上。如果该属性值的数量等于animation-name
属性值的数量,则两者一一对应;如果该属性值的数量小于animation-name
属性值的数量,则多余的动画以重复该属性的属性值,使属性值与动画一一对应;如果该属性值的数量大于animation-name
属性值的数量,则多余的属性值会被忽略。
.animated {
animation-name: translate, rotate, scale;
animation-duration: 1s;
/* 属性值数量小于动画的数量 最终等同于 */
animation-duration: 1s, 1s, 1s;
}
.animated {
animation-name: translate, rotate, scale;
animation-duration: 1s, 2s;
/* 属性值数量小于动画的数量 最终等同于 */
animation-duration: 1s, 2s, 1s;
}
.animated {
animation-name: translate, rotate, scale;
/* 属性值数量等于动画的数量 一一对应 */
animation-duration: 1s, 2s, 3s;
}
.animated {
animation-name: translate, rotate, scale;
animation-duration: 1s, 2s, 3s, 4s;
/* 属性值数量大于动画的数量 最终等同于 */
animation-duration: 1s, 2s, 3s;
/* 动画时间 4s 没有相应的关键帧动画,将会被忽略 */
}
animation-timing-function:
该属性用于指定动画执行时的速度曲线,属性值有:linear
、ease
(默认值)、ease-in
、ease-out
和 ease-in-out
,以及steps()
或cubic-bezier()
(贝塞尔曲线函数)。
animation-timing-function:linear;
-
linear
表示匀速运动,速度曲线是一条直线。 -
ease
(默认值)表示慢-快-慢,初期速度很慢,然后先逐渐加速,再快速减速,最终缓慢结束。加速过程较缓,减速过程较快。 -
ease-out
表示减速运动,先快后慢,初期速度很快,然后逐渐减速,最终缓慢结束。 -
ease-in
表示加速运动,先慢后快,初期速度很慢,然后逐渐加速,最终快速结束。 -
ease-in-out
表示慢-快-慢,初期速度慢,然后逐渐加速,中期速度最快,然后逐渐减速,最终缓慢结束。加速过程和减速过程相当。 -
linear()
函数定义了一个分段线性函数,可以在其各个点之间线性插值,从而允许你模拟出更复杂的动画效果,比如弹跳和弹簧等效果。JS或SVG可以通过linear曲线生成器 生成相应的曲线参数。 -
steps()
函数定义了分步运动,其效果相当于逐步执行过渡动画,类似于帧动画,接受两个参数,第一个参数指定的是步数,第二个参数指定的是方向。第二个参数值可以是jump-start
、jump-end
、jump-none
、jump-both
、start
和end(默认值)
,其中start
和jump-start
表现行为一样,同样的end
和jump-end
表现行为一样。函数还有两个预定义的关键字:step-start
和step-end
。前者等同于steps(1, start)
,而后者等同于steps(1, end)
。
jump-start
表示第一步动画在开始时发生;jump-end
表示最后一步动画在结束时发生;jump-both
表示在 0% 和 100% 处均出现跳跃,相当于在动画过程中加上一步;表示两端均无跳跃,而是在 0% 处和 100% 处将值各保持 1/n 的时长。更多内容可以查看:easing-function
。 -
cubic-bezier()
用于定义贝塞尔曲线,贝塞尔曲线由 P0、P1、P2 和 P3 四个点进行定义。P0 和 P3 是曲线的起点和终点,在 CSS 中,这两个点是固定的,因为坐标是成比例。P0 为 (0, 0),代表初始时间和初始状态,P3 为 (1, 1),代表最终时间和最终状态。其余的中间点 P1(x1,y1)、P2(x2,y2) 是可以动态改变的两个点,对应 cubic-bezier(x1,y1,x2,y2) 中的四个参数,通过改变 P1、P2 两点的坐标值来动态生成的贝塞尔曲线表示动画中的速度变化。 具体的贝塞尔曲线很难直接书写出来,我们可以通过贝塞尔曲线生成器,来获取对应的曲线值。或者可以通过浏览器调试工具对速度曲线进行调整。
在固定y的情况下,x越大运动越慢,在固定x情况下,y越大运动越快。y如果是负数,则元素会变小,y’如果大于1,则会变大,但最终还会恢复为1。
animation-delay:
该属性用于指定动画初始开始执行前需要延迟等待的时间,单位为s
或ms
,默认为0。如果动画会执行多次,也只会在初次执行时进行延迟等待,在后续执行时,不会再进行延迟等待。
animation-delay: 2s;
该属性设置的属性值通常为正值,但如果属性值为负数,则浏览器会按照该负数的绝对值时间,跳转到该时间对应的动画节点处开始执行动画,类似于动画从中间节点开始执行。其实负值中的负号相当于一个信号,告诉浏览器将这个值视为偏移而不是延迟,偏移到该时间对应的动画节点处开始执行动画。
如果指定的偏移时间(animtion-delay
)大于动画单次迭代的持续时间,则需要进一步考虑是否有足够的迭代次数,使动画总的执行时间大于偏移时间。如果大于等于,则初始会偏移跳转到对应的时间节点处开始执行动画;如果小于,则动画不会有执行过程,而且直接跳转到动画的最终结束状态。
<style>
@keyframes move {
0% {
background: red;
transform: translateX(20px);
}
50% {
background: yellow;
transform: translateX(200px);
}
100% {
background: red;
transform: translateX(0);
}
}
.d {
width: 180px;
height: 100px;
margin-bottom: 10px;
background: red;
/* 执行时间5秒 匀速 保留结束和开始样式 执行1次 */
animation: move 5s linear both 1;
/* 延迟时间为0 */
animation-delay: 0;
}
.d1 {
/* 延迟时间为正数 正常延迟 */
animation-delay: 3s;
}
.d2 {
/* 延迟时间为负数 且绝对值小于动画的总执行周期 延迟变为偏移 从中间1.5s节点开始执行 */
animation-delay: -1.5s;
}
.d3 {
/* 延迟时间为负数 且绝对值大于动画的总执行周期 直接展示结束状态 */
animation-delay: -6s;
}
</style>
<body>
<div class="d"></div>
<div class="d d1"></div>
<div class="d d2"></div>
<div class="d d3"></div>
</body>
页面效果:
该属性还可以设置多个动画延迟时间,属性值之间通过,
进行分隔,设置的多个属性值会按照顺序映射到对应的animation-name
中的动画上。如果该属性值的数量等于animation-name
属性值的数量,则两者一一对应;如果该属性值的数量小于animation-name
属性值的数量,则多余的动画以重复该属性的属性值,使属性值与动画一一对应;如果该属性值的数量大于animation-name
属性值的数量,则多余的属性值会被忽略。
.element {
animation-name: color, translate, scale, rotate, opacity;
animation-delay: .5s;
/* 属性值数量相等 最终等同于 */
animation-delay: .5s, .5s, .5s, .5s, .5s;
}
.element {
animation-name: color, translate, scale, rotate, opacity;
animation-delay: .5s, 1s;
/* 属性值数量小于动画的数量 最终等同于 */
animation-delay: .5s, 1s, .5s, 1s, .5s;
}
.element {
animation-name: color, translate, scale, rotate, opacity;
animation-delay: .5s, 1s, 1.5s, 2s, 2.5s;
/* 属性值数量等于动画的数量 一一对应 */
animation-delay: .5s, 1s, 1.5s, 2s, 2.5s;
}
.element {
animation-name: color, translate, scale, rotate, opacity;
animation-delay: .5s, 1s, 1.5s, 2s, 2.5s, 3s;
/* 属性值数量大于动画的数量 最终等同于 */
animation-delay: .5s, 1s, 1.5s, 2s, 2.5s; /* 多余的 3s 将会被忽略 */
}
animation-iteration-count:
该属性用于指定动画播放的次数,属性值可以为正整数或infinite
(循环无限播放),默认为1,表示动画只执行一次。
animation-iteration-count: infinite;
animation-direction:
该属性用于指定动画的执行方向,其属性值有四种:
normal
(默认值):表示动画正向执行,在每次执行中,都是从0%/from
执行到100%/to
。如果动画执行多次(animation-iteration-count
),则一次执行结束后,下一次执行时会立即回到起点(0%/from
),并重新从起点执行。reverse
:表示动画反向执行,在每次执行中,都是从100%/to
执行到0%/from
。如果动画执行多次(animation-iteration-count
),则一次执行结束后,下一次执行时会立即回到动画起点(100%/to
),并重新从起点执行。alternate
:表示动画正反交替执行。如果动画执行多次(animation-iteration-count
),则奇数次执行按正向执行,而偶数次迭代按反向执行。alternate-reverse
:表示动画反向交替执行。如果动画执行多次(animation-iteration-count
),则奇数次执行按反向执行,而偶数次迭代按正向执行。
总结一下:normal
和 reverse
两者表现行为恰好相反;alternate
和 alternate-reverse
两者表现行为恰好相反;alternate
同时具备 normal
和 reverse
两者表现特征。
animation-direction: altermate;
当该属性值为reverse
时,动画的时间函数效果也会被反转,因为设置的时间函数是相对于正向执行的。例如:ease-in
时间函数将变为 ease-out
。
该属性还会影响到animation-fill-mode
属性的最终效果,具体可以看下面的animation-fill-mode
章节。
animation-play-state:
该属性用于指定动画是否执行,默认值为running
,表示正在执行。可以设置属性值为paused
,表示暂停动画,动画会暂停执行,并保留当前时间节点的样式状态。如果再次设置属性值为running
,则会从当前状态继续执行。
// 暂停动画
animation-play-state: paused;
// 运行动画
animation-play-state: running;
该属性通常与:hover
、:focus
以及通过Js变更类名等操作结合使用,实现动画的暂停与播放。
@keyframes spin {
to {
rotate: 360deg;
}
}
.animated {
/* 默认正常执行动画 */
animation: spin 2s linear infinite;
}
.animated:hover {
/* hover时暂停动画 离开hover状态时继续执行动画 */
animation-play-state: paused;
}
还有一种常见的应用场景是:在页面中存在大量的动画时,这些动画可能会消耗大量的 CPU 和 GPU 资源。即使用户当前并未查看页面上的某些动画,它们仍然在后台运行,可能导致性能下降。可以通过暂停那些不在视窗或容器可视区中的动画,只播放位于可视区域内的动画的方式,从而减轻系统负担,提高页面整体性能。
animation-fill-mode:
该属性用于指定动画的填充模式,即在动画执行开始前/结束后,@keyframes
中声明的关键帧样式是否对目标元素有效。
该属性有四个属性值:
none
(默认值),表示在动画执行结束后@keyframes
中所有样式都无效,展示元素本身的样式。forwards
(向前取样式),表示在动画结束后,将会应用动画结束时最后一帧的样式,也就是正常情况下@keyframes
中100%/to
关键帧的样式。该关键帧位于动画结束后的前面,所以是向前取样式。backwards
(向后取样式),表示在动画开始前(包括animation-delay
时间),将会应用动画开始时第一帧的样式,也就是正常情况下@keyframes
中0%/from
关键帧的样式。该关键帧位于动画开始前的后面,所以是向后取样式。both
,表示同时遵循forwards
和backwards
的规则,在开始前应用动画开始时的样式,在结束后应用动画结束时的样式。
animation-fill-mode: both;
该属性会被animation-direction
属性和animation-iteration-count
属性影响到,在使用时要注意。
如果animation-direction
属性值为reverse
或alternate-reverse
,动画反向播放,则此时动画第一帧的样式对应100%/to
,动画的最后一帧对应0%/from
。此时该属性设置属性值为forwards
/backwards
/both
,具体的样式效果就会与正常情况相反。
animation-iteration-count
属性也是类似的道理,其效果会改变动画的最后一帧对应的样式,从而会影响到animation-fill-mode
属性的具体效果。例如下面的例子:动画的最后一个关键帧是 0%
,因为动画运行了两次,第一次迭代是从 0%
到 100%
,第二次迭代从 100%
到 0%
。
@keyframes ani {
0% {
translate: -300px;
}
100% {
translate: 300px;
}
}
.animated {
animation: ani5s linear forwards;
animation-fill-mode: forwards;
animation-direction: alternate;
animation-iteration-count: 2;
}
animation-direction
属性和animation-iteration-count
属性与最后一帧的关系,如下图所示:
animation-composition:
该属性用于指定动画合成的逻辑,即动画关键帧中的样式属性(效果值)与元素本身的同一样式属性(基础值)或者多个动画元素之前的相同样式属性的复合操作。
replace
(替换,默认值):表示将效果值覆盖基础值,页面样式为效果值。或者是在多个动画之间,最后一个动画将完全替换之前的动画效果,也就是只有最后一个动画效果会起作用。add
(叠加):表示将效果值添加到基础值之后,其效果相当于在基础值的的样式基础上继续叠加效果值的样式。或者是在多个动画之间,将多个动画的效果会叠加在一起,一起影响属性的最终效果。例如,如果一个动画元素向右移动30px
,而另一个动画使元素向左移动20px
,使用add
复合操作之后,元素将向右移动10px
。accumulate
(累计):表示将效果值与基础值累计相加。如果属性值为数值类型,则最终效果为两者相加后的样式。或者是在多个动画之间,将属性值累计相加。例如元素默认有一个filter
属性的值为blur(5px)
, 动画1的0%
位置有一个blur(10px)
,动画2的0%
位置有一个blur(20px)
,设置该属性后,0%
关键帧的filter
属性的复合值是blur(35px)
。
@keyframes move {
0% {
transform: translateX(0px);
width: 200px;
}
50% {
transform: translateX(200px);
width: 400px;
}
100% {
transform: translateX(0);
width: 200px;
}
}
.d {
width: 180px;
height: 100px;
background: red;
animation: move 5s infinite;
/* 设置效果为相加 则初始0% 和 结束100%时 的width的实际效果为380px 50%时的实际效果为580px */
animation-composition: accumulate;
}
该属性通常不在CSS中使用,都是在JavaScript中使用,结合Web动画API使用:
element.animate([
{ transform: 'translateX(0px)' },
{ transform: 'translateX(100px)' }
], {
duration: 1000,
composite: 'add'
});
更多相关内容可查看:animation-composition。
其他属性:
除了上面那些标准动画属性之外,还有一些实验性动画属性,此处就不展开讲述了,感兴趣的可以自行查看:
- animation-range:该属性用于设置动画附件范围沿其时间线的开始和结束位置,即动画沿时间线的开始和结束位置。
- animation-range-start:该属性用于设置动画的附件范围沿其时间线的开始位置,即动画沿时间线的开始位置。
- animation-range-end:该属性用于设置动画附件范围沿其时间线的结束位置,即动画沿时间线的结束位置。
- animation-timeline:该属性用于指定控制 CSS 动画进度的时间线。
animation属性简写:
除animation-composition
之外,其他animation
的相关属性都可以简写到一起,简写属性如下:
animation: 动画名称 持续时间 运动曲线 延迟等待时间 执行次数 执行方向 填充模式 执行状态
animation: animation-name animation-duration animation-timing-function animation-delay animation-iteration-count animation-direction animation-fill-mode animation-play-state;
例如:
animation: move 2s linear 1s infinite alternate forwards running;
简写属性中<time>
类型的属性值的顺序很重要,其中第一个可以被解析时间的值会被分配给animation-duration
,再次出现的第二个值,才会分配给animation-delay
。而其余属性值的顺序就不是那么重要了。
animation同时应用多个动画:
如果要给同一个元素指定多个动画,则只需要在animation
中,通过,
分隔设置多个动画的相关动画属性即可。
animation: move 2s linear 1s infinite alternate forwards, bear 2s linear·;
不同的动画之间的属性互不干扰,但具体应用到动画元素上的样式可能会有冲突覆盖。例如,一个动画试图将元素向左移动,而另一个动画同时试图将元素向右移动。此时的具体动画效果就取决于animation-composition
属性的值。
为了避免这些问题,要尽量减少多个动画同时作用于同一个元素的情况,可以通过分层动画的形式,拆分动画。例如:
<style>
@keyframes xAxis {
50% {
animation-timing-function: ease-in;
translate:450px 0;
}
}
@keyframes yAxis {
50% {
animation-timing-function: ease-out;
translate: 0 -450px;
}
}
.xAxis {
animation: xAxis 2.5s infinite linear;
}
.yAxis {
animation: yAxis 2.5s infinite linear;
}
</style>
<div class="dot xAxis">
<span class="yAxis"></span>
</div>