参加字节跳动的青训营时个人写的笔记。这部分是蒋翔老师讲的课。
动画:动画是通过快速连续排列彼此差异极小的连续图像来制造运动错觉和变化错觉的过程。
动画都需要定义两个基本状态,即起始状态和结束状态,然后填补两者之间的空白,让动画连贯。
空白的补全方法有两种:
常见的前端动画技术:Sprite 动画、CSS 动画、JS 动画、SVG 动画、WebGL 动画
CSS animation 是常见的 CSS 动画实现方式
@keyframes
定义的动画序列定义元素的平移变换。
div {
width: 200px;
height: 200px;
background-color: red;
animation: Translate infinite 4s linear;
}
@keyframes Translate {
0% {
transform: translate(0, 0);
}
25% {
transform: translate(0, 200px);
}
50% {
transform: translate(200px, 200px);
}
75% {
transform: translate(200px, 0);
}
100% {
transform: translate(0, 0);
}
}
定义元素的缩小放大
div {
width: 200px;
height: 200px;
background-color: red;
animation: Scale infinite 2s linear;
transform-origin: 0 0; /*缩放基准点*/
}
@keyframes Scale {
from {
transform: scale(1, 1);
}
to {
transform: scale(0.5, 0.5); /*第一个、第二个参数分别是x轴、y轴缩放的倍数*/
}
}
定义元素的旋转
div {
width: 200px;
height: 200px;
margin: 200px;
background-color: red;
animation: Scale infinite 4s linear;
}
@keyframes Scale {
from {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
定义元素的倾斜
div {
width: 200px;
height: 200px;
margin: 200px;
background-color: red;
animation: Translate 1s linear forwards;
}
@keyframes Translate {
from {
transform: skew(0, 0);
}
to {
transform: skew(
0,
45deg
); /* 第一个参数是水平方向的倾斜角度,第二个参数是垂直方向的倾斜角度 */
/* transform: skew(45deg, 0); */
}
}
效果(直接打开可能会看不了,可能要科学上网,蒋翔老师的这张图片好像是放到 github 上的)
优点:简单、高效。不依赖于主线程,采用硬件加速(GPU)。
缺点:不能动态修改或定义动画的内容,不同的动画无法实现同步,多个动画无法堆叠
使用场景:简单的 H5 活动/宣传页
相关库:animation.css、shake.css
这部分不知道为什么放在了 SVG 部分,不过个人又放到了 CSS 这边。(感觉以后可能能用上)
SVG 是基于 XML 的矢量图形描述语言,可以和 CSS、JS 很好的配合。
实现 SVG 动画有三种方式:
JS 笔画的原理:stroke-dashoffset、stroke-dasharray 配合使用实现笔画效果。
<line stroke-dasharray="10, 5"
x1="10" y1="10" x2="100" y2="10" />
<!-- 10像素短划线,5像素空白。起点是(10, 10), 终点是(100, 10) -->
这部分待后续填坑
不是本人写的。属于是分享链接
JS 可以实现很多复杂的动画,还可以操作 canvas 进行绘制。
JS 动画函数封装(蒋翔老师讲课用上的):
function animate({ easing, draw, duration }) {
let start = performance.now(); // 不使用Date.now()的原因是performance.now()以恒定速度自增,精确到微秒级别,不易被篡改
return new Promise((resolve) => {
requestAnimationFrame(function animate(time) {
// timeFraction goes from 0 to 1
let timeFraction = (time - start) / duration;
if (timeFraction > 1) timeFraction = 1;
// calculate the current animation state
let progress = easing(timeFraction);
draw(progress); // draw it
if (timeFraction < 1) {
requestAnimationFrame(animate);
/*
* 使用RequestAnimationFrame而不是使用setTimeout或setInterval的原因:
* 该方法允许回调函数在浏览器准备重绘时运行,而且很快
* 当页面在后台时,也就不会有重绘,所以回调函数也不会运行,所以动画会暂停,不会消耗资源
*/
} else {
resolve();
}
});
});
}
参数:
easing:缓动函数。决定执行进度在时间增加的过程中的变化,可以是线性的,也可以是非线性的
easing(timeFraction) {
return timeFraction * 100;
},
draw:绘制函数。相当于画笔,会一直反复被调用。入参是当前执行的进度 progress,是一个介于 0 到 1 之间的数字
const draw = (progress) => {
ball.style.transform = `translate(${progress}px, 0)`;
};
duration:持续时间
const draw = (progress) => {
ball.style.transform = `translate(${progress * 100}px, 0)`; // 一秒走100像素
};
animate({
easing(timeFraction) {
return timeFraction;
},
draw,
duration: 1000,
});
这部分还不是很明白,和数学挂上钩了(累)。
性能角度:页面渲染的一般过程:JS -> CSS -> 计算样式 -> 布局 -> 绘制->渲染层合并。其中,布局(重排)和绘制(重绘)是最耗时的两部分,所以应尽可能减少这两部分。