其他分享
首页 > 其他分享> > 【第二届青训营-寒假前端场】- 「前端动画实现」笔记

【第二届青训营-寒假前端场】- 「前端动画实现」笔记

作者:互联网

将自己在掘金上发的笔记搬了过来:前端动画实现个人博客

动画的基本原理

动画是什么

动画是通过快速连续排列彼此差异极小的连续图像来制造运动错觉和变化错觉的过程。

——维基百科

动画发展史

如今的前端动画技术已经普及

  1. 常见的前端动画技术
  1. 按应用分类

GIF、Flash的出现,一度成为主流,也是在00年的前后,苹果公司认为Flash会导致CPU的负载,耗电加快,宣布全面放弃Flash,所有的苹果设备电池寿命都显著提高。而如今的web动画主要由CSS、JS动画为主。

计算机动画

计算机图形学

计算机视觉的基础,涵盖点、线、面、体、场的数学构造方法。

计算机动画 是计算机图形学的分支,主要包含2D、3D动画。无论动画多么简单,始终需要定义两个基本状态,即开始状态结束状态。没有它们,我们将无法定义插值状态,从而填补两者之间的空白。

1.gif

快速√ 连续排列 × 彼此差异极小× 制造 “错觉”×

可以看到,上面这张动画只有快速,并没有制造错觉,这就不得不提到帧率这个概念了~~(打游戏的这个概念应该都熟)~~

那么接下来,填补起始点和结束点之间的空白,尝试让动画连贯。

image.png

空白的补全方式有以下两种

前端动画分类

css动画

CSS层叠样式表(Cascading Style Sheets),本身是一种样式表语言,用来描述HTML或XML(包括如SVG、MathML、XHTML之类的XML分支语言),而CSS中的 animation 属性是 animation-nameanimation-duration, animation-timing-functionanimation-delayanimation-iteration-countanimation-directionanimation-fill-modeanimation-play-state 属性的一个简写属性形式。

animation-name

animation-name 属性指定应用的一系列动画,每个名称代表一个由@keyframes定义的动画序列,其值如下

多个动画定义使用逗号分隔开即可

/* Single animation */
animation-name: none;
animation-name: test_05;
animation-name: -specific;
animation-name: sliding-vertically;

/* Multiple animations */
animation-name: test1, animation4;
animation-name: none, -moz-specific, sliding;

/* Global values */
animation-name: initial
animation-name: inherit
animation-name: unset

animation-duration

animation-duration 属性指定一个动画周期的时长。默认值为0s,表示无动画。

它的值为一个动画周期的时长,单位为秒(s)或者毫秒(ms),无单位值无效。也可以指定多个值,他的多个值与animation-name一一对应

**注意:**负值无效,浏览器会忽略该声明,但是一些早期的带前缀的声明会将负值当作0s。

/* Single animation */
animation-duration: 6s
animation-duration: 120ms

/* Multiple animations */
animation-duration: 1s, 15s
animation-duration: 10s, 30s, 230ms

animation-timing-function

animation-timing-function 属性定义CSS动画在每一动画周期中执行的节奏。可能值为一或多个,它的多个值也是与animation-name一一对应。本身CSS定义了一些缓动函数,我们可以调用这些缓动函数来达到缓入缓出的效果。

对于关键帧动画来说,timing function作用于一个关键帧周期而非整个动画周期,即从关键帧开始开始,到关键帧结束结束。

定义于一个关键帧区块的缓动函数(animation timing function)应用到该关键帧;另外,若该关键帧没有定义缓动函数,则使用定义于整个动画的缓动函数。

/* Keyword values */
animation-timing-function: ease;
animation-timing-function: ease-in;
animation-timing-function: ease-out;
animation-timing-function: ease-in-out;
animation-timing-function: linear;
animation-timing-function: step-start;
animation-timing-function: step-end;

/* Function values */
animation-timing-function: cubic-bezier(0.1, 0.7, 1.0, 0.1);
animation-timing-function: steps(4, end);
animation-timing-function: frames(10);

/* Multiple animations */
animation-timing-function: ease, step-start, cubic-bezier(0.1, 0.7, 1.0, 0.1);

/* Global values */
animation-timing-function: inherit;
animation-timing-function: initial;
animation-timing-function: unset;

animation-delay

animation-delay 定义动画于何时开始,即从动画应用在元素上到动画开始的这段时间的长度。(就是延迟多久开始)

0s是该属性的默认值,代表动画在应用到元素上后立即开始执行。否则,该属性的值代表动画样式应用到元素上后到开始执行前的时间长度;

**定义一个负值会让动画立即开始。但是动画会从它的动画序列中某位置开始。**例如,如果设定值为-1s,动画会从它的动画序列的第1秒位置处立即开始。

如果为动画延迟指定了一个负值,但起始值是隐藏的,则从动画应用于元素的那一刻起就获取起始值。

animation-delay: 3s;
animation-delay: 2s, 4ms;

animation-iteration-count

animation-iteration-count 定义动画在结束前运行的次数 可以是1次也可以是无限循环.

/* 值为关键字 */
animation-iteration-count: infinite;

/* 值为数字 */
animation-iteration-count: 3;
animation-iteration-count: 2.4;

/* 指定多个值 */
animation-iteration-count: 2, 0, infinite;

它的多值跟duration不同,它是在每个动画开始和结束的时候切换自己的执行次数

animation-direction

animation-direction 属性指示动画是否反向播放

animation-direction: normal
animation-direction: reverse
animation-direction: alternate
animation-direction: alternate-reverse
animation-direction: normal, reverse
animation-direction: alternate, reverse, normal

animation-fill-mode

animation-fill-mode 属性设置CSS动画在执行之前和之后如何将样式应用于其目标。

/* Single animation */
animation-fill-mode: none;
animation-fill-mode: forwards;
animation-fill-mode: backwards;
animation-fill-mode: both;

/* Multiple animations */
animation-fill-mode: none, backwards;
animation-fill-mode: both, forwards, none;

注意:当您在animation-*属性上指定多个以逗号分隔的值时,它们将根据值的数量以不同的方式分配给 animation-name 属性中指定的动画。 有关更多信息,请参阅设置多个动画属性值

animation-play-state

animation-play-state 属性定义一个动画是否运行或者暂停。可以通过查询它来确定动画是否正在运行。另外,它的值可以被设置为暂停和恢复的动画的重放。恢复一个已暂停的动画,将从它开始暂停的时候开始恢复,而不是从动画序列的起点开始。

/* Single animation */
animation-play-state: running;
animation-play-state: paused;

/* Multiple animations */
animation-play-state: paused, running, running;

/* Global values */
animation-play-state: inherit;
animation-play-state: initial;
animation-play-state: unset;

一个栗子:CSS BEER! (codepen.io)

去看了看这个大佬的其他项目,都很有意思!#codevember - 19 - CSS Eggs (codepen.io)Periodic Table of Elements - HTML/CSS (codepen.io)

transform API

transform 属性允许你旋转,缩放,倾斜或平移给定元素。这是通过修改CSS视觉格式化模型的坐标空间来实现的。

transform-origin 指定原点的位置,默认的转换原点是 center

transform属性可以指定为关键字值none 或一个或多个<transform-function>值。

**注意:**他只能转换由盒模型定位的元素,根据经验如果元素具有display: block,则由盒模型定位元素。

transition 过渡动画,在dom加载完成或class发生变化时触发,这个属性是 transition-propertytransition-durationtransition-timing-functiontransition-delay 的一个简写属性。

/* Apply to 1 property */
/* property name | duration */
transition: margin-right 4s;

/* property name | duration | delay */
transition: margin-right 4s 1s;

/* property name | duration | timing function */
transition: margin-right 4s ease-in-out;

/* property name | duration | timing function | delay */
transition: margin-right 4s ease-in-out 1s;

/* Apply to 2 properties */
transition: margin-right 4s, color 1s;

/* Apply to all changed properties */
transition: all 0.5s ease-out;

/* Global values */
transition: inherit;
transition: initial;
transition: unset;

keyframe实现动画

@keyframes 关键帧@keyframes at-rule规则通过在动画序列中定义关键帧(或waypoints)的样式来控制CSS动画序列中的中间步骤。和 转换 transition 相比,关键帧 keyframes 可以控制动画序列的中间步骤。

// 从左侧滑入
@keyframes slidein {
  from {
    transform: translateX(0%); 
  }
  to {
    transform: translateX(100%);
  }
}
// 
@keyframes identifier {
  0% { top: 0; }
  50% { top: 30px; left: 20px; }
  50% { top: 10px; }
  100% { top: 0; }
}

举个栗子:my CSS Animation pratice (codepen.io)

@keyframes identifier {
  0% { top: 0; left: 0; }
  50% { top: 60%; left: 60%;}
  100% {  top: 0; left: 0; }
}
@keyframes slidein {
  from {
    transform: translateX(0%); 
  }
  to {
    transform: translateX(100%);
  }
}
body  >div {
  position: absolute;
  display:flex;
  align-items:center;
  justify-content: center;
  color: #fafafa;
  background-color: #141414;
  padding: 10px;
  width:20%; height:20%;
/*   从左上到右下,持续5s,延迟1s,无限循环 */
/*   animation: identifier 5s linear 1s infinite; */
/*   向右滑,持续1s,两次 */
  animation: slidein 1s 2;
}

总结一下:

css动画的优点:简单、高效、声明式的。不依赖于主线程,采用硬件加速(GPU),通过简单的控制keyframe animation播放和暂停。

缺点:不能动态修改或定义动画,内容不同的动画无法实现同步多个动画彼此无法堆叠
适用场景:简单的h5活动/宣传页。
推荐库:Animate.cssCSShake等。

svg实现动画

svg是基于XML的矢量图形描述语言,它可以与CSS和]S较好的配合,实现svg动画通常有三种方式:SMIL、JS、CSS

第一个动画的实现原理

文字溶解原理-filter

filter 属性将模糊或颜色偏移等图形效果应用于元素。滤镜通常用于调整图像,背景和边框的渲染。基础案例:https://codepen.io/jiangxiang/pen/XWeQGQK

JS笔画原理-stroke

stroke-dashoffset、stroke-dasharray配合使用实现笔画效果。
stroke-dasharray 属性可控制用来描边的点划线的图案范式。它是一个数列,数与数之间用逗号或者空白隔开,指定短划线和缺口的长度。如果提供了奇数个值,则这个值的数列重复一次,从而变成偶数个值。因此,5,3,2等同于5,3,2,5,3,2。
stroke-dashoffset 属性指定了dash模式到路径开始的距离。
老师的示例:stroke-dasharray&stroke-dashoffset (codepen.io)

// 5px实线 5px空白 x1y1 -> x2y2
<line stroke-dasharray="5, 5" x1="10" y1="10" x2="190" y2="10" />
// 5px实线 10px空白
<line stroke-dasharray="5, 10" x1="10" y1="30" x2="190" y2="30" />
// 10px实线 5px空白
<line stroke-dasharray="10, 5" x1="10" y1="50" x2="190" y2="50" />
// 5px实线 1px空白...
<line stroke-dasharray="5, 1" x1="10" y1="70" x2="190" y2="70" />
<line stroke-dasharray="1, 5" x1="10" y1="90" x2="190" y2="90" />
<line stroke-dasharray="0.9" x1="10" y1="110" x2="190" y2="110" />
<line stroke-dasharray="15, 10, 5" x1="10" y1="130" x2="190" y2="130" />
<line stroke-dasharray="15, 10, 5, 10" x1="10" y1="150" x2="190" y2="150" />
<line stroke-dasharray="15, 10, 5, 10, 15" x1="10" y1="170" x2="190" y2="170" />
// 总长度180 180实线 180空白(全是实线) 这个时候改变dashoffset的值就可以实现笔画效果
<line stroke-dasharray="180" stroke-dashoffset的值就可以实现笔画效果="0" x1="10" y1="190" x2="190" y2="190" />

image.png

直线这种比较简单的可以直接知道其总长度进而通过初始化dashoffset实现笔画效果,那么不规则图形?

通过path.getTotalLength();

path路径 - d属性定义
大写字母跟随的是绝对坐标x,y,小写为相对坐标dx,dyM/m绘制起始点。

老师的例子:SVG 使用stroke-dashoffset和stroke-dashoffset实现笔画效果 (codepen.io)

js实现动画

JS可以实现复杂的动画,可以操作css、svg也可以操作canvas动画API上进行绘制。

如何做选择?

CSS实现

JS 实现

总结:

实现前端动画

js动画函数封装

function animate({easing, draw, duration})    {
  let start = performance.now(); // 取当前时间
  return new Promise(resolve => {
    requestAnimationFrame(function animate(time) {
      let timeFraction = (time - start) / duration;
      if(timeFraction > 1) timeFraction = 1;
      
      let progress = easing(timeFraction);

      draw(progress);
      
      if(timeFraction < 1) {
          requestAnimationFrame(animate);
      } else {
          resolve();
      }
    });
  });
}

这个函数中首先通过performance.now()取到当前系统时间,它以一个恒定的速率慢慢增加,不会受到系统时间的影响以浮点数的形式表示时间,精度最高可达微秒级,不易被篡改。入参说明:

简单动画

JS执行动画的核心思想

∆r = ∆v∆t

简单理解:r为距离,v速度,t是时间。通过比例系数缩放来保证动画的真实感。

举个例子:匀速运动,老师的例子非常全面JS封装动画函数 (codepen.io)(一定要去看看!)

const ball = document.querySelector('.ball');
const draw = (progress) => {
    ball.style.transform = `translate(${progress}px, 0)`;
}
// 沿x轴匀速运动
animate({
    duration: 1000,
    easing(timeFraction) {
        return timeFraction * 100;
    },
    draw
})

1.gif

// 重力
const gravity = () => {
  const draw = (progress) => {	// 高度500
    ball.style.transform = `translate(0, ${500 * (progress - 1)}px)`;
  };
  animate({
    duration: 1000,
    easing(timeFraction) {
      return timeFraction ** 2;	// t平方
    },
    draw,
  });
};

2.gif

// 摩擦力
const friction = () => {
  // 初始高度500px
  const draw = (progress) => {
    ball.style.transform = `translate(0, ${500 * (progress - 1)}px)`;
  };
  animate({
    duration: 1000,
    easing(timeFraction) {
      // 初始速度系数为2
      return timeFraction * (2 - timeFraction);
    },
    draw,
  });
};

3.gif

// 平抛 x
const horizontalMotion = () => {
  const draw = (progress) => {
    ball.style.transform = `translate(${500 * progress.x}px, ${500 * (progress.y - 1)}px)`;
  };

  // 有两个方向,沿着x轴匀速运动,沿着y轴加速运动
  animate({
    duration: 1000,
    easing(timeFraction) {
      return {
        x: timeFraction,
        y: timeFraction ** 2,
      }
    },
    draw,
  });
};

4.gif

其余的还有很多,再多的话就在easing返回的对象中加入新的属性,如旋转:

6.gif

复杂动画

自动衰减:更加复杂

8.gif

9.gif

相关实践资源

动画代码示例:

设计网站:

动画制作工具(一般都是UE、UI同学使用):

SVG:

JS:

CSS:

canvas:

在实际工作中往往是将UI给的动画帧/设计文件转化为代码

优化

页面渲染的一般过程为JS -> CSS -> 计算样式 --> 布局 -> 绘制 -> 渲染层合并。

其中,Layout(重排)和Paint(重绘)是整个环节中最为耗时的两环,所以我们尽量避免这两个环节。从性能方面考虑,最理想的渲染流水线是没有布局和绘制环节的,只需要做渲染层的合并即可。

建议

ps:学到了!这就去改改

总结感想

今天的课也非常的硬核,介绍了前端动画的基本原理、前端动画的分类和如何实现前端的动画,介绍了相关资源与实践方法。让我对前端动画有了一个更深刻的了解,最后给出的资源推荐也很有帮助~

本文引用的内容大部分来自蒋翔老师的课、MDN(CSS动画属性的介绍翻了半天MDN,上面写的非常全面并且有很生动的例子,推荐一看)

标签:动画,前端,transform,timeFraction,寒假,青训营,progress,animation,CSS
来源: https://blog.csdn.net/qq_45890533/article/details/122631533