聊一聊前端动画的种类,以及动画的触发方式有哪些?
引言
动画在前端开发中扮演着重要的角色。它不仅可以提升用户体验,还可以使界面更加生动和有趣。在这篇文章中,我们将深入探讨前端动画的各种实现方式,包括 CSS 动画、JavaScript 动画、SVG 动画等。我们还将讨论一些触发动画的方式和动画在用户体验中的最佳实践。
前端动画分类
-
CSS 动画
- CSS Transition
CSS 过渡,属于补间动画,即设置关键帧的初始状态,然后在另一个关键帧改变这个状态,比如大小、颜色、透明度等,浏览器将自动根据二者之间帧的值创建的动画。 - CSS Animation
CSS 动画,可以理解是CSS Transition
的加强版,它既可以实现 补间动画 的动画效果,也可以使其以 逐帧动画 的方式进行绘制。
- CSS Transition
-
SVG 动画
- SVG 动画用于矢量图形,提供了高质量的动画效果,常用于图标和图形动画。可以使用 SMIL 在SVG中定义动画。同样的也可以使用css或者js来控制svg动画。
-
Canvas 动画
- 通过结合使用
requestAnimationFrame
、路径和变换等技术对画布的元素进行擦除和重新绘制,可以实现复杂的动画效果。另外Canvas还可以用于绘制复杂的背景或静态内容,从而减少每帧的绘制工作量。 - 可以参考我的一篇关于canvas制作动画的文章:用Canvas绘制一个高可配置的圆形进度条
- 通过结合使用
-
JS 动画
-
setTimeout / setInterval / requestAnimationFrame
setTimeout
和setInterval
这两个 API 设定的时间会因为浏览器当前工作负载而有所偏差,而且无法与浏览器的绘制帧保持同步。所以才有了 与浏览器的绘制帧同步 的原生 APIrequestAnimationFrame
,以取代setTimeout
和setInterval
实现动画。 -
Web Animations API
浏览器动画 API,通过 JavaScript 操作。这些 API 被设计成CSS Transition
和CSS Animation
的接口,很容易通过 JS 的方式实现 CSS 动画,它是对动画化的支持最有效的方式之一。
-
css 动画
css过渡动画 transition
注意
由于浏览器是根据样式差异化的两帧自动计算并过渡,所以 transition
只支持可识别中间值的属性 (如大小、颜色、位置、透明度等),而如 display 属性则不支持。
语法定义
-
transition-property
: 指定哪个或哪些 CSS 属性用于过渡。只有指定的属性才会在过渡中发生动画,其他属性仍如通常那样瞬间变化。 -
transition-duration
: 指定过渡的时长。你可以为所有属性指定一个值,或者指定多个值,或者为每个属性指定不同的时长。 -
transition-timing-function
: 指定一个缓动函数,定义属性值怎么变化。常见的缓动函数是一个三次贝塞尔曲线 (cubic-bezier(<x1>, <y1>, <x2>, <y2>)
)。当然也可以选择关键字- linear:
cubic-bezier(0.0, 0.0, 1.0, 1.0)
- ease:
cubic-bezier(0.25, 0.1, 0.25, 1.0)
- ease-in:
cubic-bezier(0.42, 0.0, 1.0, 1.0)
- ease-out:
cubic-bezier(0.0, 0.0, 0.58, 1.0)
- ease-in-out:
cubic-bezier(0.42, 0.0, 0.58, 1.0)
- linear:
-
transition-delay
: 指定延迟,即属性开始变化时与过渡开始发生时之间的时长。
代码示例
/* 单条 简写形式 */
transition:
<property> <duration> <timing-function> <delay>;
/* 多条 简写形式 */
transition:
<property> <duration> <timing-function> <delay>,
<property> <duration> <timing-function> <delay>,
...;
/* 单条 子属性形式 */
transition-property: <property-name>;
transition-duration: <duration-time>;
transition-timing-function: <timing-function>;
transition-delay: <duration-time>;
/* 多条 子属性形式 */
transition-property: <property-name> [, <property-name>, ...];
transition-duration: <duration-time> [, <duration-time>, ...];
transition-timing-function: [, <cubic-bezier>, ...];
transition-delay: [, <duration-time>, ...];
// 如果任意属性值列表的长度比其他属性值列表要短,则其中的值会重复使用以便匹配
// 如果某个属性的值列表长于 `transition-property` 的属性,则将被截短
css过渡动画 触发方式
1. 伪类触发(:hover、:focus、:active等)
.button {
background-color: blue;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: red;
}
2. 类名切换(通过JS动态切换类名来触发过渡效果)
<button id="toggleButton">Toggle</button>
<div id="box" class="box"></div>
<style>
.box {
width: 100px;
height: 100px;
background-color: blue;
transition: background-color 0.3s ease;
}
.box.active {
background-color: red;
}
</style>
<script>
document.getElementById('toggleButton').addEventListener('click', function() {
document.getElementById('box').classList.toggle('active');
});
</script>
3. 属性变化
<button id="toggleButton">Toggle</button>
<div id="box" class="box"></div>
<style>
.box {
width: 100px;
height: 100px;
background-color: blue;
transition: background-color 0.3s ease;
}
</style>
<script>
document.getElementById('toggleButton').addEventListener('click', function() {
const box = document.getElementById('box');
box.style.backgroundColor = box.style.backgroundColor === 'red' ? 'blue' : 'red';
});
</script>
4. 伪元素触发(通过伪元素如::before
、::after
的状态变化来触发过渡效果。)
<div class="box"></div>
<style>
.box {
width: 100px;
height: 100px;
position: relative;
}
.box::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: blue;
transition: background-color 0.3s ease;
}
.box:hover::before {
background-color: red;
}
</style>
css动画 animation
注意
CSS Animation 具备了对 关键帧和循环次数 的自定义能力。CSS Animation 在实现像 CSS Transition 补间动画 效果时,还可以在起始帧和结束帧之间自定义中间帧,使得动画更加平滑过渡的同时,对动画有了更好的控制和自定义能力。
语法定义
先创建一个带名称的 @keyframes
规则,以便后续使用 animation-name
属性将动画同其关键帧声明进行匹配。每个规则包含多个关键帧,也就是一段样式块语句,每个关键帧有一个百分比值作为名称,代表在动画进行中,在哪个阶段触发这个帧所包含的样式。
-
animation-name
:指定一个或多个 @keyframes 的名称,描述了要应用于元素的动画。多个 @keyframes 以逗号分隔。 -
animation-duration
:设置动画完成一个动画周期所需的时间,需要指定单位,如1s
、500ms
。 -
animation-delay
:指定执行动画之前的等待时间。动画可以稍后开始、立即从开头开始、立即在动画中途播放 (如-1s
) 。其中-1s
意思是动画立即从 1s 处开始。 -
animation-iteration-count
:设置动画序列在停止前应播放的次数,有效值0
、正整数、正小数、无限循环infinite
。 -
animation-direction
:设置动画是正向播放normal
、反向播放reverse
、正向交替播放alternate
、反向交替播放alternate-reverse
。 -
animation-play-state
:设置动画是运行还是暂停,有效值running
、paused
。 -
animation-fill-mode
:设置 CSS 动画在执行之前和之后如何将样式应用于其目标,有效值如下:none
:当动画未执行时,动画将不会将任何样式应用于目标,而是已经赋予给该元素的 CSS 规则来显示该元素。这是默认值forwards
:目标将保留由执行期间遇到的最后一个关键帧计算值。backwards
:动画将在应用于目标时立即应用第一个关键帧中定义的值。
animation-timing-function
:设置动画在每个周期的持续时间内如何进行,主要是如下两种函数:
-
cubic-bezier
三次贝塞尔曲线 (cubic-bezier(<x1>, <y1>, <x2>, <y2>)
),以实现 补间动画 效果。 -
steps
是一个分段的阶跃函数,,以实现 逐帧动画。n 相当于单次动画的帧数,每帧动画的时间是均等的 (steps(n, <jumpterm>)
),其中jumpterm (默认值 end)
含义如下:- jump-start:在起始位置阶跃,
n=2 ⇒ 50% 100%; (100 / 2)
- jump-end:在结束位置阶跃,
n=4 ⇒ 0% 25% 50% 75%; (100 / 4)
- jump-none:起止位置均无跳跃,
n=5 ⇒ 0% 25% 50% 75% 100%; (100 / 4)
- jump-both:起止位置均有跳跃
n=3 ⇒ 25% 50% 75%; (100 / 4)
- start:等同 jump-start
- end:等同 jump-end
- step-start:等同 steps(1, jump-start)
- step-end:等同 steps(1, jump-end)
- jump-start:在起始位置阶跃,
/* animation 声明样式顺序 */
/* animation-duration */
/* animation-easing-function */
/* animation-delay */
/* animation-iteration-count */
/* animation-direction */
/* animation-fill-mode */
/* animation-play-state */
/* animation-name */
animation: 3s ease-in 1s 2 reverse both paused slidein;
/* animation - duration | easing-function | delay | name */
animation: 3s linear 1s slidein;
/* more animations - duration | easing-function | delay | name */
animation: 3s linear slidein, 3s ease-out 5s slideout;
/* animation-name */
animation-name: none;
animation-name: animate1;
animation-name: animate1, animate2;
/* animation-timing-function */
animation-timing-function: ease;
animation-timing-function: step-start;
animation-timing-function: cubic-bezier(0.1, 0.7, 1, 0.1);
animation-timing-function: ease, step-start, cubic-bezier(0.1, 0.7, 1, 0.1);
css animation 动画触发方式
和css transition
触发动画方式相似
此外还可以增加一个图层,专门用于制作动画效果。
例如:鼠标在点击按钮时,会有涟漪动画。
// 涟漪动画定义
@keyframes ripple {
0% {
transform: scale(0);
opacity: 1;
}
to {
transform: scale(4);
opacity: 0;
}
}
// 图层动画 css
.ripple {
position: absolute;
border-radius: 50%;
background: rgba(8, 7, 7, 0.2);
pointer-events: none;
animation: ripple 0.6s linear;
}
// 制作动画 这样每次点击按钮 就会生成动画,动画结束便销毁动画元素
const makeAnimate = (e: React.MouseEvent) => {
const dom = e.currentTarget;
const rect = dom.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const size = 100;
const ripple = document.createElement('span');
ripple.classList.add('ripple');
ripple.style.width = `${size}px`;
ripple.style.height = `${size}px`;
ripple.style.left = `${x - size / 2}px`;
ripple.style.top = `${y - size / 2}px`;
dom.appendChild(ripple);
ripple.addEventListener('animationend', () => {
ripple.remove();
});
};
svg 动画
常用的 SMIL 动画元素
<animate>
:用于动画化单个属性。<animateTransform>
:用于动画化变换属性,如旋转、缩放、平移等。<animateMotion>
:用于沿着路径动画化元素。(路径动画)<set>
:用于在指定时间点设置属性值。
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" fill="red">
<animate attributeName="cx" from="50" to="150" dur="2s" repeatCount="indefinite" />
</circle>
</svg>
svg 描边动画
SVG动画的路径实现主要依赖属性:stroke
(描边)和 fill
(填充)。
- stroke:定义svg的轮廓线。常用css属性有:
stroke-dasharray
(描边的样式),stroke-dashoffset
(起始位置),stroke-color
(描边的颜色),stroke-opacity
(描边的透明度),stroke-linecap
(描边端点形状)等。 - fill:定义svg内部颜色或图案 ,常用css属性有
fill-opacity
(定义填充的透明度),fill-rule
(定义填充规则)等。
stroke-dasharray (定义虚线的长度和间隔)
提供一个奇数或偶数数列,其中数与数之间用逗号或空格隔开,用来指定短划线和缺口的长度,并重复。 如果是偶数数列
,则一个表示短线长度,一个表示缺口长度。 如果是奇数数列
,将奇数数列复制一个变成偶数数列,然后按照短线,缺口的顺序绘制。
(偶数数列) stroke-dasharray="5, 5" x1="10" y1="10" x2="190" y2="10"
表示从坐标(10,10)到(200,10)这条水平线上,短划线和缺口都为5个px
(奇数数列) stroke-dasharray="20 10 5" x1="10" y1="10" x2="190" y2="10"
表示从坐标(10,10)到(200,10)这条水平线上,短划线和缺口按照20 10 5 20 10 5的顺序排列。
stroke-dashoffset (定义虚线的起始位置)
stroke-dashoffset 属性用于指定路径开始的距离(正值向左偏移,负值向右偏移)
描边动画示例:svg描边动画
js 动画
setTimeout / setInterval API
设定定时器,通过周期性的触发重复执行绘制动画的函数,来实现 “逐帧动画” 的效果。
-
优势
- 具有很好的浏览器兼容性
-
劣势
- 只能接近设备屏幕刷新率,无法做到和浏览器同步,所以可能会存在卡顿、丢帧、抖动的现象
- 由于浏览器单线程机制,存在队列中回调函数被阻塞的可能,所以无法保证每一次调用的时间间隔都相同,某次回调可能会被跳过,导致跳帧。
requestAnimationFrame API
为了弥补 setTimeout / setInterval
在动画方面的不足,浏览器提供了为动画而生的 API,它可以让 DOM 动画、Canvas 动画、 SVG 动画等有一个统一的刷新机制,随着浏览器的屏幕刷新,统一绘制动画帧。
let id = null
// 动画函数
const draw = () => {
/* 动画绘制... */
}
const start = () => {
draw()
cancelAnimationFrame(id)
id = requestAnimationFrame(start)
}
const stop = () => { cancelAnimationFrame(id) }
-
优势
- 由系统来决定回调函数的执行时机, 它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次, 这样就不会引起丢帧现象, 也不会导致动画出现卡顿的问题。
- 在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU的开销。
-
不足
- 同 setTimeout/setInterval 一样,它是以逐帧动画的方式进行绘制,无法做到像 CSS 动画,让游览器自动根据两帧之间的差异创建插值,以实现补间动画的过渡效果。
Web Animations API
requestAnimationFrame
、setTimeout/setInterval
都是以逐帧绘制的方式实现动画, 而 Animations API 不仅可以 “逐帧动画”,还可以实现 “补间动画” 的效果。- CSS 动画有一定的局限性,需要事先预设动画样式,而且无法与 JS 进行交互。相比之下,Animations API 可以随时定义并使用动画,自然是更加灵活方便。
参考文档: https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API
语法示例:
const element = document.getElementById("container");
const animation = element.animate(
[
{ transform: "translateY(0%)" },
{ transform: "translateY(100%)" },
],
{ duration: 3000, fill: "forwards" }
);
关于Filp动画
浏览器计算位置很快,绘制可能很慢。利用浏览器强大的计算能力,获取动画的起止状态,接着单独开启一个线程做动画。这样触发布局更新的操作,只会发生在一帧时间内,剩下的动画跑在单独的线程上,会更流畅。
介绍下FLIP 。
- F 代表 First,也就是动画的开始状态。
- L 代表 Last,代表动画结束状态。
- I 代表 Invert,也就是状态反转,使用 transform 等属性,创建单独的图层,并将元素状态反转回去。
- P 代表 Play,播放动画。
示例代码:
其中,在初始帧中,应用逆变换(translate
和 scale
),将元素从其最终状态逆变换到初始状态。
最后一帧 transform: "none"
的作用是将元素的变换属性重置为其最终状态。具体来说,transform: "none"
表示不应用任何变换,这意味着元素将恢复到由 CSS 设置的最终位置和大小。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FLIP Animation Example</title>
<style>
#box {
width: 100px;
height: 100px;
background-color: #4caf50;
position: absolute;
}
</style>
</head>
<body>
<div id="box"></div>
<button id="animateButton" style="margin-top: 300px;">Animate</button>
<script>
const box = document.getElementById('box');
const button = document.getElementById('animateButton');
button.addEventListener('click', () => {
// First: 记录初始状态
const first = box.getBoundingClientRect();
// 修改元素的位置
box.style.top = `${300}px`;
box.style.left = `${300}px`;
// Last: 记录最终状态
const last = box.getBoundingClientRect();
// Invert: 计算初始状态和最终状态之间的变换
const deltaX = first.left - last.left;
const deltaY = first.top - last.top;
const deltaW = first.width / last.width;
const deltaH = first.height / last.height;
// 应用 FLIP 动画
box.animate(
[
{
transformOrigin: "top left",
transform: `
translate(${deltaX}px, ${deltaY}px)
scale(${deltaW}, ${deltaH})`,
},
{
transformOrigin: "top left",
transform: "none",
},
],
{
duration: 300,
easing: "ease-in-out",
fill: "both",
}
);
});
</script>
</body>
</html>
原文地址:https://blog.csdn.net/weixin_47339511/article/details/140615823
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!