Animate动画时空机制

重要通知

动画的实现原理,就是在时间维度上对空间(X方向、Y方向、Z方向)进行不同值的变换。

基本概况

简介与核心思想

动画: 移动、缩放、旋转 为了衡量画面切换速度,引入了每秒帧数 FPS(Frames Per Second)的概念,是指每秒画面重绘的次数。FPS 越大,则动画效果越平滑,当 FPS 小于 20 时,一般就能明显感受到画面的卡滞现象

60fps(每秒帧数) 浏览器每帧16ms

3D旋转轮播图 | 相册图

  • https://www.jq22.com/demo/jquery3dlb201801292252

  • https://www.jq22.com/demo/jquery3Dlbt201801160003/

  • https://www.jq22.com/demo/jQuery3dLbt201709270842/

  • https://www.jq22.com/demo/jquery3d20151116/

  • https://www.jq22.com/demo/jquery-3dlb20150817/

  • https://www.jq22.com/demo/jquerylbt202109061025/

  • https://www.jq22.com/demo/jQuery-xc-150422204120/

  • html5酷炫的3D签到墙动画特效: https://www.17sucai.com/preview/1/2017-10-09/3D/index.html

  • http://47.99.130.140/project/admin/#/project/admin/home/other/loading

  • 事件

transitionend:

Anime.js、Velocity.js等动画库

三大动画时间事件

window.setInterval

在浏览器中,setTimeout()/setInterval() 的每调用一次定时器的最小间隔是4ms

  • [注意] Window系统下表现为15毫秒;Forefix浏览器表现为10毫秒;Chrome浏览器表现为1毫秒
var intervalID = setInterval(func, [delay, arg1, arg2, ...]);
var intervalID = setInterval(function[, delay]);
var intervalID = setInterval(code, [delay]);  

WindowTimers.clearInterval(intervalID);

执行

let time = 0;

const interval = window.setInterval(() => {
  time += 1;

  if (time >= 2) {
    window.clearInterval(interval);
  }

  console.info("time: ", time);
}, 1000);

window.setTimeout

在浏览器中,setTimeout()/setInterval() 的每调用一次定时器的最小间隔是4ms 最小延迟时间:

  var timeoutID = scope.setTimeout(function[, delay, arg1, arg2, ...]);
  var timeoutID = scope.setTimeout(function[, delay]);
  var timeoutID = scope.setTimeout(code[, delay]);  

  window.clearTimeout(timeoutID);
  • window.setImmediate: 该特性是非标准的,请尽量不要在生产环境中使用它!
    • 该方法用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数。
    • [注意] Window系统下表现为15毫秒;Forefix浏览器表现为10毫秒;Chrome浏览器表现为1毫秒
var immediateID = setImmediate(func, [param1, param2, ...]);
var immediateID = setImmediate(func);  

window.clearImmediate(immediateID);
  • 正常遍历输出
// 可以遍历输出: 0 1 2 3 4
for (var i = 0; i < 5; i++) {
  console.log(new Date, i);
}
  • 同步和异步代码的区别、变量作用域、闭包

缺陷: 不能分别遍历出i的不同值

async function Case1() {
  for (var i = 0; i < 5; i++) {
    setTimeout(function() {
      console.log(new Date, i);
    }, 1000);
  }
  console.log('out', i);
}
  • 改造输出5 -> 0,1,2,3,4 闭包
async function Case2() {
  for (var i = 0; i < 5; i++) {
    (function(i) {
      setTimeout(function() {
        console.log(new Date, i);
      }, 1000);
    })(i);
  }
  console.log('out', i);
}

setTimeout API接口

IE9 及其更早版本不支持

async function Case3() {
  for (var i = 0; i < 5; i++) {
    setTimeout(function(i) {
      console.log(new Date, i);
    }, 1000, i);
  }
  console.log('out', i);
}

requestAnimationFrame(Fn)

  • 浏览器在下次重绘之前调用指定的回调函数更新动画,回调函数执行次数通常是每秒 60 次。
  • 该时间戳是一个十进制数,单位为毫秒,最小精度为 1ms(1000μs)。

window.requestAnimationFrame(Fn)

window.requestAnimationFrame = window.requestAnimationFrame ||
                               window.mozRequestAnimationFrame ||
                               window.webkitRequestAnimationFrame ||
                               window.oRequestAnimationFrame ||
                               window.msRequestAnimationFrame;

window.cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;

兼容浏览器解决方案

;(function () {
  var lastTime = 0
  var vendors = ['ms', 'moz', 'webkit', 'o']
  for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']
    window.cancelAnimationFrame =
      window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']
  }

  if (!window.requestAnimationFrame)
    window.requestAnimationFrame = function (callback, element) {
      var currTime = new Date().getTime()
      var timeToCall = Math.max(0, 16 - (currTime - lastTime))
      var id = window.setTimeout(function () {
        callback(currTime + timeToCall)
      }, timeToCall)
      lastTime = currTime + timeToCall
      return id
    }

  if (!window.cancelAnimationFrame)
    window.cancelAnimationFrame = function (id) {
      clearTimeout(id)
    }
})() 

window.cancelAnimationFrame()

播放器案例

// 播放器对象
let initRequestAnimation = null;

// 播放序列控制器
const playerControl = {
  paused: true, // 播放/暂停状态
  durations: 0, // 总时长
  prevDuration: 0, // 上次进度值,节流机制
  currentDuration: 0, // 进度值
  durationsText: "", // 总时长00:00:00文本
  processText: "" // 进度时长00:00:00文本
};

// 时间序列控制器变量
const timingControl = {
  startPlayTimestamp: 0, // 开始播放时的当前时间戳, 精确度与单位为毫秒级
  accumulateTimestamp: 0, // 上一个暂停播放时的播放累积时长的时间戳, 精确度与单位为毫秒级,适于节流问题,否则一秒会执行60次
  currentTimestamp: 0 // 截止目前的累积播放时间戳, 精确度与单位为毫秒级
};

// 自动播放控制器
async function updateDurationListener() {
  const { startPlayTimestamp, accumulateTimestamp } = timingControl;
  const timestamp = performance.now();

  // 计算从播放开始时 到 当前时间戳的播放时长
  const stageTimestamp = timestamp - startPlayTimestamp;

  // 计算整体播放的累积时长 时间戳
  timingControl.currentTimestamp = accumulateTimestamp + stageTimestamp;

  // 计算整体播放的累积时长 总秒数
  const currentSeconds = Math.floor(timingControl.currentTimestamp / 1000);
  playerControl.currentDuration = currentSeconds;

  // 节流机制,禁止一秒内执行60次,限制一秒执行一次
  if (playerControl.currentDuration !== playerControl.prevDuration) {
    playerControl.prevDuration = playerControl.currentDuration;

    // 播放业务
    await updateActionByDurations();
  }
}

// 播放控制器
const requestAnimationFrame = window.requestAnimationFrame;
const cancelAnimationFrame = window.cancelAnimationFrame;

async function startRequestAnimation() {
  initRequestAnimation.value = requestAnimationFrame(async function animate() {
    initRequestAnimation.value = requestAnimationFrame(animate);
    await updateDurationListener();
  });
}

async function stopRequestAnimation() {
  cancelAnimationFrame(initRequestAnimation.value);

  // 暂停业务
  pauseAction();
}


/**
 * 根据播放累积时长同步更新业务
 */
async function updateActionByDurations() {
  // 
}


// 暂停演示 业务
async function pauseAction() {
  cancelAnimationFrame(initRequestAnimation.value);

  playerControl.paused = true;

  // 清除开始播放时的时间戳
  timingControl.startPlayTimestamp = 0;

  // 记录暂停时已经播放的累积时长
  timingControl.accumulateTimestamp = playerControl.currentDuration * 1000;

  await manageAudio(false);
}

CSS3 动画与相关类库

Animate.css

代码示例

<html>
  <body>
    <h1 class="animate__animated animate__bounce"></h1>
    <h2 class="animate__animated animate__delay-2s"></h2>
    <h3 class="animate__animated"></h3>
    <h4 class="animate__animated"></h4>
    <h5 class="animate__animated"></h5>
    <h6 class="animate__animated"></h6>
  </body>
</html>

延迟

animate__delay-2s	# 2s
animate__delay-3s	# 3s
animate__delay-4s	# 4s
animate__delay-5s	# 5s

速度

animate__slow	  # 2s
animate__slower	# 3s
animate__fast	  # 800ms
animate__faster	# 500ms

重复

animate__repeat-1	# 1
animate__repeat-2	# 2
animate__repeat-3	# 3
animate__infinite	# infinite

时间周期需要自己写CSS

.animate__animated.animate__bounce {
  --animate-duration: 2s;
}
/* This changes all the animations globally */
:root {
  --animate-duration: 800ms;
  --animate-delay: 0.9s;
}

源码原理

@keyframes backInUp {
  0% {
    -webkit-transform: translateY(1200px) scale(0.7);
    transform: translateY(1200px) scale(0.7);
    opacity: 0.7;
  }

  80% {
    -webkit-transform: translateY(0px) scale(0.7);
    transform: translateY(0px) scale(0.7);
    opacity: 0.7;
  }

  100% {
    -webkit-transform: scale(1);
    transform: scale(1);
    opacity: 1;
  }
}
.animate__backInUp {
  animation-name: backInUp;
}

bounce.js

Effeckt.css

move.js

Dynamics.js

Dynamics.js是用于创建基于物理的动画的 JavaScript 库,能为你提供9种标准的动效,你可以制定其中的持续时间、频率、预期尺寸和强度等数据,创造出符合物理效果的动效。

TweenMax.js

TweenMax.js: 适用于移动端和现代互联网的超高性能专业级动画插件, Tweenmax是GreenSock 动画平台的核心,配合其他插件,可动画CSS属性、滤镜效果、 颜色、 声音、 色彩、 帧、 饱和度、 对比度、 色调、 色系、 亮度、 贝塞尔等。

  • 官网: https://www.tweenmax.com.cn/

tweenJS

文件类型

  • UMD : tween.umd.js

  • AMD : tween.amd.js

  • CommonJS : tween.cjs.js

  • ES6 Module : tween.es.js

  • 代码示例

import { TWEEN } from '/Animate/tweenJS/dist/tween.module.min.js';


requestAnimationFrame(animate() {
  requestAnimationFrame(animate)
  TWEEN.update()
});

// 初始状态
const coords = {x: 0, y: 0};
const tween = new TWEEN.Tween(coords) 
  // 目标状态
  .to({x: 300, y: 200}, 1000) 
  // 延时执行时间
  .delay(1000)
  // 动画特征类型
  .easing(TWEEN.Easing.Quadratic.Out) 
  // 状态变化处理函数
  .onUpdate(() => {
    box.style.setProperty('transform', `translate(${coords.x}px, ${coords.y}px)`)
  })
  // 完成事件
  .onComplete(() => {
    console.log('完成');
  })
  // 停止事件
  .onStop(() => {
    console.log('停止');
  })
  // 重复次数
  .repeat(10)
  // 每次重复延迟时间
  .repeatDelay(1000)
  // 启动动画
  .start();

API接口

var exports = {
  Easing: Easing,
  Group: Group,
  Interpolation: Interpolation,
  now: now$1,
  Sequence: Sequence,
  nextId: nextId,
  Tween: Tween,
  VERSION: VERSION,
  getAll: getAll,
  removeAll: removeAll,
  add: add,
  remove: remove,
  update: update,
};

动画类型 | Easing

每个效果都分三个缓动方式,分别是: easeIn: 从0开始加速的缓动,也就是先慢后快;easeOut: 减速到0的缓动,也就是先快后慢;easeInOut: 前半段从0开始加速,后半段减速到0的缓动。

  • Linear: 线性匀速运动效果;
  • Quadratic: 二次方的缓动(t^2);
  • Cubic: 三次方的缓动(t^3);
  • Quartic: 四次方的缓动(t^4);
  • Quintic: 五次方的缓动(t^5);
  • Sinusoidal: 正弦曲线的缓动(sin(t));
  • Exponential: 指数曲线的缓动(2^t);
  • Circular: 圆形曲线的缓动(sqrt(1-t^2));
  • Elastic: 指数衰减的正弦曲线缓动;
  • Back: 超过范围的三次方缓动((s+1)t^3 – st^2);
  • Bounce: 指数衰减的反弹缓动。

TWEEN控制

start和stop Tween.start和Tween.stop分别用于控制tween动画的开始和结束。 对于已经结束和没有开始的动画,Tween.stop方法不起作用。Tween.start方法同样接收一个时间参数。如果你使用了该参数,tween动画将在延时该时间数后才开始动画。否则它将立刻开始动画 update: 可以通过TWEEN.update方法来执行动画的更新。

chain: 如果你想制作多个多行,例如: 一个动画在另一个动画结束后开始。可以通过chain方法来使实现。如下的代码,tweenB 在 tweenA 之后开始动画: tweenA.chain(tweenB): 可以像下面这样制作一个无限循环的动画: tweenA.chain(tweenB);tweenB.chain(tweenA);

repeat: 如果你想制作循环动画可以使用chain来实现,但是更好的方法是使用repeat方法。它接收一个用于描述你想循环多少次的参数: tween.repeat(10); // repeats 10 times and stops tween.repeat(Infinity); // repeats forever 永久执行循环动画

yoyo: 这个函数只在你使用repeat方法是起作用。当它被激活时,tween 的效果类似yoyo效果。该效果是动画会在开始或结束处向反方向反弹。 delay: delay方法用于控制动画之间的延时 ``` tween.delay(1000); tween.start(); onStart: tween开始动画前的回调函数。 onStop: tween结束动画后的回调函数。 onUpdate: 在tween每次被更新后执行。 onComplete: 在tween动画全部结束后执行。

Loading

DIV+CSS3动画 | Loading : http://www.h-ui.net/icon/loading.shtml 炫酷的Loading效果插件(还包括其他多功能): https://shnhz.github.io/shn-ui/#/component/epic-spinners HTML5+CSS3 最酷的 loading 效果收集 https://www.runoob.com/w3cnote/free-html5-css3-loaders-preloaders.html https://www.jq22.com/yanshi4405

https://www.17sucai.com/pins/demo-show?id=32053 https://www.17sucai.com/pins/demo-show?id=28013 30种CSS3炫酷页面预加载loading动画特效 https://www.17sucai.com/preview/869565/2017-11-17/cssLoading/index.html

53种css3 loading白色加载图标动画特效 https://www.17sucai.com/pins/demo-show?id=24122 纯css3彩色的loading加载图标动画特效 https://www.17sucai.com/pins/demo-show?id=25577 纯css3音阶波浪loading加载动画特效 https://www.17sucai.com/pins/demo-show?id=22606 纯css3精美的loading网页加载动画特效 https://www.17sucai.com/pins/demo-show?id=23717

html5 SVG绘图动画制作页面加载loading绘图动画效果 https://www.17sucai.com/pins/demo-show?id=4160

GSAP动画库

简介与核心思想

GSAP,即,是现代网络动画的标准,一个强大的 JavaScript 工具集,可将开发人员变成动画超级英雄,GSAP 是地球上最快的全功能脚本动画工具。

Tween(补间)

gsap.to(".box", { rotation: 27, x: 100, duration: 1 });
  • gsap.from()

  • gsap.fromTo()

Timeline(时间线)

// 创建时间线实例
const tl = gsap.timeline();

tl.to(".box1", {duration: 2, x: 100})
  .to(".box2", {duration: 1, y: 200})
  .to(".box3", {duration: 3, rotation: 360});


//WITH Timelines (cleaner, more versatile)
var tl = gsap.timeline({repeat: 2, repeatDelay: 1});
tl.to("#id", {x: 100, duration: 1});
tl.to("#id", {y: 50, duration: 1});
tl.to("#id", {opacity: 0, duration: 1});

// then we can control the whole thing easily...
tl.pause();
tl.resume();
tl.seek(1.5);
tl.reverse();
  • gsap.pause()
  • gsap.play()
  • gsap.progress()
  • gsap.restart()
  • gsap.resume()
  • gsap.reverse()
  • gsap.seek()
  • gsap.time()
  • gsap.duration()
  • gsap.timeScale()
  • gsap.kill()

GreenSock 动画平台核心工具

插件列表

SplitText: 将文本块拆分为行、单词和字符,使您能够轻松地为每个部分制作动画。| https://greensock.com/docs/v3/Plugins/SplitText Draggable: 添加拖放任何元素的能力。| https://greensock.com/docs/v3/Plugins/Draggable MorphSVGPlugin: 复杂 SVG 路径的平滑变形。| https://greensock.com/morphSVG/ | https://greensock.com/docs/v3/Plugins/MorphSVGPlugin DrawSVGPlugin: 动画 SVG 笔划的长度和位置。 | https://greensock.com/drawSVG/ | https://greensock.com/docs/v3/Plugins/DrawSVGPlugin MotionPathPlugin: 沿路径为任何元素设置动画。| https://greensock.com/docs/v3/Plugins/MotionPathPlugin GSDevTools Inertia MotionPathHelper

PhysicsProps ScrambleText

Physics2D

https://www.tweenmax.com.cn/Physics2DPlugin/ Physics2DPlugin 是GreenSock 动画平台用于进行物理动画的拓展插件,包括重力、速度、加速度、摩擦力动画等等。

FLIP

https://aerotwist.com/blog/flip-your-animations/

kuteJS

官网: http://thednp.github.io/kute.js/

Lottie

官网: http://airbnb.io/lottie/#/

  • 安装配置

npm install lottie-web

  • 代码示例 <script src="https://test.ysunlight.com/ServerCore/source/lottie-web/lottie.min.js" type="text/javascript"></script>
  const lottieAnim = lottie.loadAnimation({
    container: element, // the dom element that will contain the animation
    renderer: 'canvas',
    loop: true,
    autoplay: true,
    path: 'https://labs.nearpod.com/bodymovin/demo/markus/halloween/markus.json',
    animationData: animationData, // the animation data
    rendererSettings: {
      context: canvasContext, // the canvas context
      scaleMode: 'noScale',
      clearCanvas: false,
      progressiveLoad: false, // Boolean, only svg renderer, loads dom elements when needed. Might speed up initialization for large number of elements.
      hideOnTransparent: true //Boolean, only svg renderer, hides elements when opacity reaches 0 (defaults to true)
    }
  });

方法

lottie.play(): lottie.stop(): lottie.pause(): lottie.setLocationHref(locationHref):
lottie.setSpeed(): lottie.setDirection(): lottie.goToAndStop(value, isFrame): 跳到某个时刻/帧并停止 lottie.goToAndPlay(value, isFrame): 跳到某个时刻/帧并播放 lottie.playSegments(segments, forceFlag): 以帧为单位,播放指定片段 lottie.setSubframe(flag): lottie.searchAnimations(): lottie.loadAnimation(): lottie.destroy(): lottie.registerAnimation(): lottie.setQuality():

事件 Events

onComplete: onLoopComplete: onEnterFrame: onSegmentStart:

钩子函数

complete: loopComplete: enterFrame: segmentStart: config_ready: 完成初始配置后 data_failed: 当无法加载动画的一部分时 loaded_images: 当所有图像加载成功或错误时 data_ready: 当动画的所有部分都已加载时 DOMLoaded: 将元素添加到DOM时 destroy: 当动画挂载对象销毁时

  // 代码示例
  lottieAnim.addEventListener('data_ready', () => { 
    //
  });
Last Updated:
Contributors: 709992523