前端学习记录
  • 前言及目录
  • 前端基础
    • HTML
    • CSS
      • CSS学习之布局
    • JavaScript
      • 跟着月影学JavaScript
      • JavaScript之对象、原型链及继承
      • JavaScript中的类
      • onclick与addEventListener区别
      • JS手撕题
    • HTTP与浏览器
      • HTTP实用指南
      • Web开发的安全之旅
    • 通用知识
      • 前端必须知道的开发调试知识
      • 前端设计模式应用
      • Web 标准与前端开发
  • 数据结构及算法
    • 数据结构
      • 1、线性表(List)
      • 2、堆栈(Stack)
      • 3、队列(Queue)
      • 4、二叉树(Binary Tree)
      • 5、二叉搜索树与平衡二叉树(BST & AVL)
      • 6、堆(Stack)& 哈夫曼树 & 并查集
      • 7、图(Graph)
        • 图论——解决最小生成树问题(Kruskal算法&Prim算法)
      • 8、排序(sort)
      • 9、散列表(hash)
      • 数据结构习题
        • 第一周:最大子列和算法、二分查找
        • 第二周:线性结构
        • 第三周:栽树(二叉树等)
        • 第四周:二叉搜索树&二叉平衡树
        • 第五周:堆&哈夫曼树&并查集
        • 第六周:图(上)连通集 、DFS&BFS
        • 第七周:图(中)Floyd算法求最短路
        • 第八周:图(下)
        • 第九周:排序(上)归并&堆排序
        • 第十周:排序(下)
        • 第十一周:散列查找 & KMP
    • CS基础
      • 编译原理 实验一 词法分析器设计
      • 编译原理 实验二 LL(1)分析法程序
    • LeetCode
      • 冲刺春招-精选笔面试 66 题大通关
        • day1:21. 合并两个有序链表、146. LRU 缓存、25. K 个一组翻转链表
        • day2:14. 最长公共前缀、3. 无重复字符的最长子串、124. 二叉树中的最大路径和
        • day3:206. 反转链表、199. 二叉树的右视图、bytedance-016最短移动距离
        • day4:1. 两数之和、15. 三数之和、42. 接雨水
        • day5:7. 整数反转、215. 数组中的第K个最大元素、23. 合并K个升序链表
        • day6:33. 搜索旋转排序数组、54. 螺旋矩阵、bytedance-006. 夏季特惠
        • day7:53. 最大子数组和、152. 乘积最大子数组、41. 缺失的第一个正数
        • day8:20. 有效的括号、200. 岛屿数量、76. 最小覆盖子串
        • day9:105. 从前序与中序遍历序列构造二叉树、103. 二叉树的锯齿形层序遍历、bytedance-010. 数组组成最大数
        • day10:94. 二叉树的中序遍历、102. 二叉树的层序遍历、394. 字符串解码
        • day11:415. 字符串相加、5. 最长回文子串、72. 编辑距离
        • day12:64. 最小路径和、300. 最长递增子序列、bytedance-004. 机器人跳跃问题
        • day13:88. 合并两个有序数组、31. 下一个排列、4. 寻找两个正序数组的中位数
        • day14:121. 买卖股票的最佳时机、56. 合并区间、135. 分发糖果
        • day15:232. 用栈实现队列、22. 括号生成、128. 最长连续序列
        • day16:bytedance-007. 化学公式解析、129. 求根节点到叶节点数字之和、239. 滑动窗口最大值
        • day17:141. 环形链表、236. 二叉树的最近公共祖先、92. 反转链表 II
        • day18:322. 零钱兑换、198. 打家劫舍、 bytedance-003. 古生物血缘远近判定
        • day19:160. 相交链表、143. 重排链表、142. 环形链表 II
        • day20:704. 二分查找、43. 字符串相乘、bytedance-002. 发下午茶
        • day21题目:69. x 的平方根、912. 排序数组、887. 鸡蛋掉落
        • day22:151. 颠倒字符串中的单词、46. 全排列、2. 两数相加
      • 剑指 Offer
        • 剑指offer day1 栈与队列(简单)
        • 剑指offer day2 链表(简单)
        • 剑指offer day3 字符串(简单)
        • 剑指offer day4 查找算法(简单)
        • 剑指offer day5 查找算法(中等)
        • 剑指offer day6 搜索与回溯算法(简单)
        • 剑指offer day7 搜索与回溯算法(简单)
        • 剑指offer day8 动态规划(简单)
        • 剑指offer day9 动态规划(中等)
        • 剑指offer day10 动态规划(中等)
        • 剑指offer day11 双指针(简单)
        • 剑指offer day12 双指针(简单)
        • 剑指offer day13 双指针(简单)
        • 剑指offer day14 搜索与回溯算法(中等)
        • 剑指offer day15 搜索与回溯算法(中等)
        • 剑指offer day16 排序(简单)
        • 剑指offer day17 排序(中等)
      • 剑指 Offer 专项突击版
  • 前端进阶
    • React
      • 响应式系统与 React
      • React学习小记
      • Redux学习之Redux三原则、createSore原理及实现
    • Vue
    • TypeScript
      • TypeScript入门
      • TypeScript 类型体操练习
        • Easy题(13/13)
        • Middle(20/72)
    • 前端工程化
      • Webpack知识体系
    • Node
    • 前端动画与绘图
      • WebGL基础
      • 前端动画简介
      • Floating UI 使用经验分享 - Popover
      • Floating UI 使用经验分享 - Dialog
      • Three.js 学习
        • 学习记录
        • 资源记录
    • 前端性能优化
    • 跨端
      • RN 学习小记之使用 Expo 创建项目
    • 开源
    • SEO 优化
      • 搜索引擎优化 (SEO) 新手指南笔记
  • 笔面试记录
    • 面经集锦
      • 2022春暑期实习MetaApp一二面面经
      • 2022春暑期实习字节前端一面凉经
    • 笔试复盘
      • 2022春暑期实习-美团前端-笔试
      • 2022春暑期实习-360前端-笔试(AK)
      • 2022春暑期实习-京东前端-笔试
      • 2022春暑期实习-网易雷火前端-笔试(AK)
      • 2022春暑期实习-网易互联网前端-暑期实习笔试
由 GitBook 提供支持
在本页
  • 动画的基本原理
  • 前端动画分类
  • 实现前端动画
  • 相关实践资源
  • 总结感想

这有帮助吗?

在GitHub上编辑
导出为 PDF
  1. 前端进阶
  2. 前端动画与绘图

前端动画简介

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

动画的基本原理

动画是什么

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

——维基百科

  • 快速

  • 连续排列

  • 彼此差异极小

  • 制造 “错觉” 的过程

动画发展史

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

  1. 常见的前端动画技术

    • Sprite动画、CSS动画、JS动画、SVG动画和WebGL动画

  2. 按应用分类

    • UI动画、基于Web的游戏动画和动画数据可视化

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

计算机动画

计算机图形学

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

  • 几何和图形数据的输入、存储和压缩。

  • 描述纹理、曲线、光影等算法。

  • 物体图形的数据输出(图形接口、动画技术),硬件和图形的交互技术。

  • 图形开发软件的相关技术标准。

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

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

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

  • 帧:连续变换的多张画面,其中的每一幅画面都是一帧。

  • 帧率:用于度量一定时间段内的帧数,通常的测量单位是FPS(frame per second)。

  • 帧率与人眼:一般每秒10-12帧人会认为画面是连贯的,这个现象称为视觉暂留。对于一些电脑动画和游戏来说,低于 30 FPS 会感受到明显卡顿,目前主流的屏幕、显卡输出为60FPS,效果会明显更流畅。

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

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

  • 补间动画

    • 传统动画中,主画师绘制关键帧,交给清稿部门 ,清稿部门的补间动画师补充关键帧进行交付

  • 逐帧动画(Frame By Frame)

    • 从词语来说意味着全片每一帧逐帧都是纯手绘。(如css 的steps实现精灵动画)

前端动画分类

css动画

animation-name

  • none (初始值)特殊关键字,表示无关键帧。可以不改变其他标识符的顺序而使动画失效,或者使层叠的动画样式失效。

  • IDENT 标识动画的字符串,由大小写敏感的字母a-z、数字0-9、下划线(_)和/或横线(-)组成。第一个非横线字符必须是字母,数字不能在字母前面,不允许两个横线出现在开始位置。

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

/* 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

它的值为一个动画周期的时长,单位为秒(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

对于关键帧动画来说,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

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

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

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

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

animation-iteration-count

  • infinite

    无限循环播放动画

  • <number>

    动画播放的次数;默认值为1。可以用小数定义循环,来播放动画周期的一部分:例如,0.5 将播放到动画周期的一半。不可为负值。

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

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

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

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

animation-direction

  • normal (默认值)

    每个循环内动画向前循环,换言之,每个动画循环结束,动画重置到起点重新开始,这是默认属性。

  • alternate

    动画交替反向运行,反向运行时,动画按步后退,同时,带时间功能的函数也反向,比如,ease-in 在反向时成为 ease-out。计数取决于开始时是奇数迭代还是偶数迭代

  • reverse

    反向运行动画,每周期结束动画由尾到头运行。

  • alternate-reverse

    反向交替, 反向开始交替

    动画第一次运行时是反向的,然后下一次是正向,后面依次循环。决定奇数次或偶数次的计数从1开始。

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

/* 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;
  • none (默认)

    当动画未执行时,动画将不会将任何样式应用于目标,而是已经赋予给该元素的 CSS 规则来显示该元素。这是默认值。

  • forwards

  • backwards

  • both

    动画将遵循forwards和backwards的规则,从而在两个方向上扩展动画属性。(就是上述二者兼有)

animation-play-state

  • running

    当前动画正在运行。

  • paused

    当前动画已被停止。

/* 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;

transform API

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

  • 要应用的一个或多个CSS变换函数。 变换函数按从左到右的顺序相乘,这意味着复合变换按从右到左的顺序有效地应用。

    • // 沿x轴缩小为50%
      transform: scale(0.5);
      // 沿x轴缩小为50%,沿y轴放大为之前的2倍
      transform: scale(0.5, 2);
    • transform: rotate(30deg);
    • 一个参数时表示水平方向的倾斜角度(ax);

      两个参数时表示水平、垂直(ax, ay)。

      transform: skew(ax)       
      transform: skew(ax, ay)
  • none

    不应用任何变换。

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

/* 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 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; }
}
@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播放和暂停。

svg实现动画

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

  • SMIL:同步多媒体集成语言

    • 结论∶兼容性不理想,这里不过多讨论,当然有polyfill的方案:https://github.com/ericwilligers/svg-animation

  • 使用JS来操作SVG动画自不必多说,目前也有很多现成的类库。例如老牌的Snap.svg以及anime.js,都能让我们快速制作SVG动画。当然,除了这些类库,HTML本身也有原生的 Web Animation 实现。使用Web 8622Animation也能让我们方便快捷地制作动画。这是老师的两个栗子:

    • 文字形变: https://codepen.io/jiangxiang/pen/MWmdjeY

第一个动画的实现原理

文字溶解原理-filter

  • blur逐渐变小,在blur快没有的时候将其opacity设为0隐藏掉,就可以实现一个溶解效果

JS笔画原理-stroke

// 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" />

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

通过path.getTotalLength();

  • L/l绘制一条线段。 C/c为绘制贝塞尔曲线。 Z/z将当前点与起始点用直线连接。

  • 计算path的长度- path.getTotalLength();

  • 计算path上某个点的坐标- path.getPointAtLength(lengthNumber);

  • svg动画的优点∶通过矢量元素实现动画,不同的屏幕下均可获得较好的清晰度。可以实现一些特殊的效果:描字,形变,墨水扩散等。

  • 缺点∶使用方式较为复杂,过多使用可能会带来性能问题。

js实现动画

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

如何做选择?

CSS实现

  • 优点

    • 浏览器会对CSS3动画做一些优化,导致CSS3动画性能上稍有优势(新建一个图层来跑动画)

    • CSS3动画的代码相对简单

  • 缺点

    • 动画控制上不够灵活

    • 兼容性不佳

    • 部分动画无法实现(视差效果、滚动动画)

  • 对于简单动画都可以用css实现

JS 实现

  • 优点

    • 使用灵活,同样在定义一个动画的keyframe序列时,可以根据不同的条件调节若干参数(JS动画函数)改变动画方式。(CSS会有非常多的代码冗余)

    • 对比与CSS的keyframe粒度更粗,Css本身的时间函数是有限的,这块 JS 都可做弥补。

    • CSS很难做到两个以上的状态转化(要么使用关键帧,要么需要多个动画延时触发,再想到要对动画循环播放或暂停倒序等,复杂度极高)

  • 缺点

    • 使用到 JS 运行时,调优方面不如 CSS 简单,CSS调优方式固定。

    • 对于性能和兼容性较差的浏览器,CSS可以做到优雅降级,而 **JS 需要额外代码兼容。**会影响打包后产物的体积。

总结:

  • 当为UI元素采用较小的独立状态时,使用CSS.

  • 在需要对动画进行大量控制时,使用JavaScript。

  • 在特定的场景下可以使用SVG,可以使用CSS或JS去操作SVG变化。(比如上述的溶解、笔画效果等)

实现前端动画

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();
      }
    });
  });
}
  • draw 绘制函数

    • 可以将其想象为一只画笔,随着函数执行,这个画笔的函数会被反复的调用,并传入当前执行的进度progress,progress取决于easing的值,如若为线性增加的则为0~1。如:

      const ball = document.querySelector('.ball');
      const draw = (progress) => {
          ball.style.transform = `translate(${progress}px, 0)`;
      }
  • easing 缓动函数

    • 缓动函数改变(或者说扭曲)动画的时间,将其改为线性/非线性,或者多维度的。如:

      easing(timeFraction) {
          return timeFraction ** 2;   //timeFraction的平方
      }
  • duration 持续时间 顾名思义,单位是毫秒

  • 最后返回Promise的原因:

    • 动画可以是连续的,Promise支持通过then函数或await进行顺序调用,可以很容易地拿到这个动画的终态

  • 这个动画函数实现一个有限时间的动画封装

  • **RequestAnimationFrame (rAF) ** vs SetTimeout vs Setlnterval

    • 重排:若渲染树的一部分更新,且尺寸变化,就会发生重排。

      重绘:部分节点需要更新,但不改变其他集合形状。如改变某个元素的visibility、outline、背景颜色等,就会发生重绘。

简单动画

JS执行动画的核心思想

∆r = ∆v∆t

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

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
})
  • 重力:h = g * t ^2^

t^2^// 重力
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,
  });
};
  • 摩擦力:时间变为2t - t^2^

// 摩擦力
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,
  });
};
  • 平抛 (x轴匀速,y轴加速) 也就是说,y轴类似重力中的t^2^而x轴速度不变

// 平抛 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,
  });
};

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

  • 旋转+平抛

    // 旋转 + 平抛
    const horizontalMotionWidthRotate = () => {
      const draw = (progress) => {
        ball.style.transform = `translate(${500 * progress.x}px, ${500 * (progress.y - 1)}px) rotate(${2000 * progress.rotate}deg)`;// 这里的2000也是比例系数
      };
    
      // 有两个方向,沿着x轴匀速运动,沿着y轴加速运动
      animate({
        duration: 2000,
        easing(timeFraction) {
          return {
            x: timeFraction,
            y: timeFraction ** 2,
            rotate: timeFraction,
          }
        },
        draw,
      });
    };
  • 拉弓(x轴匀速,y轴初始速度为负的匀加速)

    // 拉弓
    const arrowMove = () => {
      // 抽象出来,初始值为2,到某个临界点变为正的1并且匀速增加
      const back = (x, timeFraction) => {
        return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x);
      }
      const draw = (progress) => {
        ball.style.transform = `translate(${200*progress.x}px, ${ - 500 * progress.y}px)`;
      };
      animate({
        duration: 1000,
        easing(timeFraction) {
          return {
            x: timeFraction,
            y: back(2, timeFraction),
          };
        },
        draw,
      });
    };
    • ps:讲到这里开始逐渐硬核起来了,tql

    // 贝塞尔
    const bezier = () => {
      const bezierPath = (x1, y1, x2, y2, timeFraction) => {
        const x = 3 * x1 * timeFraction * (1 - timeFraction) ** 2 + 3 * x2 * timeFraction ** 2 * (1 - timeFraction) + timeFraction ** 3;
        const y = 3 * y1 * timeFraction * (1 - timeFraction) ** 2 + 3 * y2 * timeFraction ** 2 * (1 - timeFraction) + timeFraction ** 3;
        return [x, y];
      }
    
      const draw = (progress) => {
        const [px, py] = bezierPath(0.2, 0.6, 0.8, 0.2, progress);
        // 实际绘制在两个维度上
        ball.style.transform = `translate(${300 * px}px, ${-300 * py}px)`;
      }
    
      animate({
        duration: 2000,
        easing(timeFraction) {
          return timeFraction * (2 - timeFraction);
        },
        draw,
      });
    }

复杂动画

  • 弹跳小球(缓动函数实现/自动衰减实现)

自动衰减:更加复杂

  • 椭圆运动

    • 套公式即可,x = a*cos(a),y = b * sin(a)

      // 椭圆
      const ellipsis = () => {
        const draw = (progress) => {
          const x = 150 * Math.cos(Math.PI * 2 * progress);
          const y = 100 * Math.sin(Math.PI * 2 * progress);
          ball.style.transform = `translate(${x}px, ${y}px)`;
        }
        
        animate({
          duration: 2000,
          easing(timeFraction) {
            return timeFraction * (2 - timeFraction);
          },
          draw,
        });
      };
      

相关实践资源

动画代码示例:

设计网站:

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

  • 2D : Animate CC、After Effects

  • 3D :Cinema 4D、Blender、Autodesk Maya

SVG:

  • Snap.SVG - 现代SVG图形的JavaScript库

  • Svg.js - 用于操作和动画SVG的轻量级库。

JS:

  • GSAP - JavaScript动画库。

  • TweenJS - 一个简单但功能强大的JavaScript补间/动画库。CreateJS库套件的一部分。

  • Velocity - 加速的JavaScript动画。

CSS:

  • Animate.css - CSS动画的跨浏览器库。像一件简单的事情一样容易使用。

canvas:

  • EaselJS - EaselJS 是一个用于在HTML5中构建高性能交互式2D内容的库

  • Fabric.js -支持动画的JavaScript画布库。

  • Paper.js -矢量图形脚本的瑞士军刀

  • Scriptographer - 使用HTML5

    Canvas移植到JavaScript和浏览器。

  • Pixijs –使用最快、最灵活的2D WebGL渲染器创建精美的数字内容。

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

  • 需要完全前端自己设计自己开发时:

    • 使用已封装好的动画库,从开发成本和体验角度出发进行取舍

  • 设计不是很有空时:

    • 清晰度、图片格式可以指定,动画尽量给出示意或者相似案例参考。索要精灵图资源等需要帮忙压缩。(移动端的资源适配等)

  • 设计资源充足时:

    • 要求设计导出lottie格式文件

      Lottie是可应用于Android, ios, Web和Windows的库, 通过Bodymovin解析AE动画,并导出可在移动端和web端渲染动画的json文件。

      import lottie from 'lottie-web' ;
      this.animation = lottie.loadAnimation({
          container: this.animationRef.current,
          renderer: 'svg',
          loop: false,
          autoplay: false,
          aninationData: dataJson,
          path: URL,
      });

优化

  • 性能角度

    • 重点:减少重绘、重排,这是整个环节中最为耗时的两环。

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

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

建议

  • 在实际的应用里,最为简单的一个注意点就是,触发动画的开始不要用display:none属性值,因为它会引起Layout、Paint环节,通过切换类名就已经是一种很好的办法。

ps:学到了!这就去改改

  • CSS3硬件加速又叫做GPU 加速,是利用GPU进行渲染,减少CPU操作的一种优化方案。由于GPU中的transform等CSS属性不会触发repaint,所以能大大提高网页的性能。CSS中的以下几个属性能触发硬件加速:

    • transform

    • opacity

    • filter

    • Will-change

    • 如果有一些元素不需要用到上述属性,但是需要触发硬件加速效果,可以使用一些小技巧来诱导浏览器开启硬件加速。

    • -算法优化 -线性函数代替真实计算-几何模型优化 -碰撞检测优化622 -内存/缓存优化-离屏绘制

总结感想

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

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

上一页WebGL基础下一页Floating UI 使用经验分享 - Popover

最后更新于3年前

这有帮助吗?

1.gif
image.png

(类比到这里,前端动画的补间动画师由浏览器来担任,如,)

CSS层叠样式表(Cascading Style Sheets),本身是一种样式表语言,用来描述HTML或XML(包括如SVG、MathML、XHTML之类的XML分支语言),而CSS中的 属性是 ,, ,,,, 和 属性的一个简写属性形式。

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

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

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

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

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

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

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

目标将保留由执行期间遇到的最后一个计算值。 最后一个关键帧取决于和的值(就是最后一个关键帧是什么样子后面就一直会是这个样子)

动画将在应用于目标时立即应用第一个关键帧中定义的值,并在期间保留此值。(这个很重要,delay几s进行的) 第一个关键帧取决于的值:animation-directionfirst relevant keyframenormal or alternate``0% or from``reverse or alternate-reverse``100% or to

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

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

一个栗子:

去看了看这个大佬的其他项目,都很有意思!、

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

指定原点的位置,默认的转换原点是 center 。

(缩放)注意其中心为 (缩放)注意其中心为

(旋转) 将元素在不变形的情况下旋转到原点周围(如 属性所指定) 。 移动量由指定角度定义;如果为正值,则运动将为顺时针,如果为负值,则为逆时针 。 180°的旋转称为点反射 (point reflection)。

(倾斜) 参数表示倾斜角度,单位deg

过渡动画,在dom加载完成或class发生变化时触发,这个属性是 ,, 和 的一个简写属性。

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

举个栗子:

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

Path实现写字动画:

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

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

image.png

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

老师的例子:

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

是一个对象,它代表了一个异步操作的最终完成或者失败。

使用!为什么?

该内置方法允许设置回调函数以在浏览器准备重绘时运行。通常这很快,但确切的时间取决于浏览器。 setTimeout和setInterval当页面在后台时,根本没有重绘,所以回调不会运行:**动画将被暂停并且不会消耗资源。**参考:

举个例子:匀速运动,老师的例子非常全面(一定要去看看!)

1.gif
2.gif
3.gif
4.gif

6.gif

贝塞尔曲线 、

直接去看老师的例子:,自动衰减这里填了之前的一个坑:为什么要使用Promise,在每次执行完毕是,会将句柄再交给上面的函数判断是否有速度的衰减,直到速度为0的时候会自动结束。

8.gif
9.gif

可以提供很多设计灵感

方便引入SDK

我自己推荐一手figma:

通过可以查询CSS属性及其影响的环节

@keyframes
transition
animation
animation-name
animation-duration
animation-timing-function
animation-delay
animation-iteration-count
animation-direction
animation-fill-mode
animation-play-state
animation-name
@keyframes
animation-duration
animation-timing-function
animation-delay
animation-iteration-count
animation-direction
animation-fill-mode
关键帧
animation-direction
animation-iteration-count
animation-delay
animation-direction
animation-name
设置多个动画属性值
animation-play-state
CSS BEER! (codepen.io)
#codevember - 19 - CSS Eggs (codepen.io)
Periodic Table of Elements - HTML/CSS (codepen.io)
transform
transform-origin
transform-function
scale
transform-origin
transform-origin
rotate
transform-origin
skew
transition
transition-property
transition-duration
transition-timing-function
transition-delay
@keyframes
转换 transition
my CSS Animation pratice (codepen.io)
Animate.css
CSShake
SVG 写字动画 (codepen.io)
filter
stroke-dasharray
stroke-dashoffset
stroke-dasharray&stroke-dashoffset (codepen.io)
path路径
d
SVG 使用stroke-dashoffset和stroke-dashoffset实现笔画效果 (codepen.io)
performance.now()
Promise
requestAnimationFrame
javascript - requestAnimationFrame loop not correct FPS - Stack Overflow
JS封装动画函数 (codepen.io)
cubic-bezier(0,2.11,1,.19) ✿ cubic-bezier.com
Animated Bézier Curves - Jason Davies
JS封装动画函数 (codepen.io)
CodePen
CodeSandbox
Dribbble - Discover the World’s Top Designers & Creative Professionals
Figma
动画的12项基本法则 - 维基百科,自由的百科全书 (wikipedia.org)
CSS Triggers
image.png
image.png
5.gif
10.gif
11.gif
7.gif