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 = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
window.cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;

var val = 0;
function test() {
  val += 1;
  console.log(val);

  if (val < 10) {
    window.requestAnimationFrame(test);
  } else {
    window.cancelAnimationFrame(test);
  }
}
window.requestAnimationFrame(test);

代码示例: 一秒定时

window.requestAnimationFrame =
  window.requestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.msRequestAnimationFrame;
window.cancelAnimationFrame =
  window.cancelAnimationFrame || window.mozCancelAnimationFrame;

var timmingConf = {
  animate: null,
  seconds: 0,
};

function animayteModelFunc() {
  //
}

function animateFunc() {
  // 毫秒
  var timestamp = performance.now();
  var seconds = Math.floor(timestamp / 1000);

  // 限制 1s 执行一次
  if (timmingConf.seconds != seconds) {
    timmingConf.seconds = seconds;

    animayteModelFunc();
  }

  // 取消动画
  window.cancelAnimationFrame(timmingConf.animate);

  // 启动定时
  timmingConf.animate = window.requestAnimationFrame(animateFunc);
}

timmingConf.animate = window.requestAnimationFrame(animateFunc);

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的缓动。淡入淡出,两端慢中间快
<style type="text/css">
div {
  position: relative;
  width: 100px;
  height: 100px;
  background-color: #f00;
}
</style>

<div></div>


<script type="text/javascript">
  document.addEventListener(
    "click",
    function (e) {
      animateFunc();
    },
    false,
  );

  window.requestAnimationFrame =
    window.requestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.msRequestAnimationFrame;
  window.cancelAnimationFrame =
    window.cancelAnimationFrame || window.mozCancelAnimationFrame;

  var timmingConf = {
    animate: null,
    history: 0,
    timestamp: 0,
    limit: 3000,
    startX: 0,
    endX: 800,
  };


  function updateXFunc(el, value) {
    el.setAttribute(
      "style",
      `transform: translateX(${value}px);-ms-transform: translateX(${value}px);-webkit-transform: translateX(${value}px);-moz-transform: translateX(${value}px);`,
    );
  }


  function animateFunc(func) {
    var divEl = document.querySelector("div");

    // 重置初始值
    window.cancelAnimationFrame(timmingConf.animate);
    timmingConf.history = performance.now();
    updateXFunc(divEl, 0);

    function animateLoopFunc(now) {
      timmingConf.timestamp = now - timmingConf.history;

      if (timmingConf.timestamp > timmingConf.limit) {
        timmingConf.timestamp = timmingConf.limit;
        timmingConf.history = now;
        window.cancelAnimationFrame(timmingConf.animate);
      } else {
        timmingConf.animate = window.requestAnimationFrame(animateLoopFunc);
      }

      // 运动函数
      var value = linearEasingUtil(
        timmingConf.startX,
        timmingConf.endX,
        timmingConf.limit,
      )(timmingConf.timestamp);

      updateXFunc(divEl, value);
    }

    timmingConf.animate = window.requestAnimationFrame(animateLoopFunc);
  }
</script>

通用参数说明

# ---------------------------------------------------------------
  参数	含义	类型	备注
# ---------------------------------------------------------------
  t	当前时间 / 进度	Number	范围:0 ≤ t ≤ d
  b	开始值	Number	动画的开始值
  c	结束值	Number	动画的结束值
  d	总时长 / 总进度	Number	动画的总执行时间 / 总步数
# ---------------------------------------------------------------

Linear: 线性匀速运动效果;

无快慢变化,匀速运动,是最基础的缓动效果,仅 1 种模式。

// 线性淡入(与线性一致,无加速)
function linearEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = x;
  return b + factor * delta;
}

// 线性淡出(与线性一致,无减速)
function linearEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = x;
  return b + factor * delta;
}

// 线性淡入淡出(全程匀速)
function linearEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = x;
  return b + factor * delta;
}

Quadratic: 二次方的缓动(t^2);

先慢后快,基于二次方(t²)的缓动,快慢变化比线性更柔和,包含 3 种模式。

// 二次方淡入(加速)
function quadEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = x * x;
  return b + factor * delta;
}

// 二次方淡出(减速)
function quadEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = 1 - (1 - x) * (1 - x);
  return b + factor * delta;
}

// 二次方淡入淡出(先加速后减速)
function quadEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  let factor;
  if (x < 0.5) {
    factor = 2 * x * x;
  } else {
    factor = 1 - 2 * (1 - x) * (1 - x);
  }
  return b + factor * delta;
}

Cubic: 三次方的缓动(t^3);

基于三次方(t³)的缓动,快慢变化比二次方更明显。

// 三次方淡入(加速,比二次方更明显)
function cubicEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = x * x * x;
  return b + factor * delta;
}

// 三次方淡出(减速,比二次方更明显)
function cubicEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = 1 - (1 - x) * (1 - x) * (1 - x);
  return b + factor * delta;
}

// 三次方淡入淡出(先加速后减速)
function cubicEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  let factor;
  if (x < 0.5) {
    factor = 4 * x * x * x;
  } else {
    factor = 1 - 4 * (1 - x) * (1 - x) * (1 - x);
  }
  return b + factor * delta;
}

Quartic: 四次方的缓动(t^4);

基于四次方(t⁴)的缓动,快慢变化更极致。

// 四次方淡入
function quartEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = x ** 4;
  return b + factor * delta;
}

// 四次方淡出
function quartEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = 1 - (1 - x) ** 4;
  return b + factor * delta;
}

// 四次方淡入淡出
function quartEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  let factor;
  if (x < 0.5) {
    factor = 8 * x ** 4;
  } else {
    factor = 1 - 8 * (1 - x) ** 4;
  }
  return b + factor * delta;
}

Quintic: 五次方的缓动(t^5);

基于五次方(t⁵)的缓动,是幂次缓动中变化最剧烈的类型。

// 五次方淡入
function quintEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = x ** 5;
  return b + factor * delta;
}

// 五次方淡出
function quintEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = 1 - (1 - x) ** 5;
  return b + factor * delta;
}

// 五次方淡入淡出
function quintEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  let factor;
  if (x < 0.5) {
    factor = 16 * x ** 5;
  } else {
    factor = 1 - 16 * (1 - x) ** 5;
  }
  return b + factor * delta;
}

Sinusoidal: 正弦曲线的缓动(sin(t));

基于正弦函数的缓动,变化曲线平滑自然,接近物理运动的惯性效果。

// 正弦淡入
function sineEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = 1 - Math.cos(x * Math.PI / 2);
  return b + factor * delta;
}

// 正弦淡出
function sineEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = Math.sin(x * Math.PI / 2);
  return b + factor * delta;
}

// 正弦淡入淡出(最平滑的基础缓动)
function sineEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = -(Math.cos(Math.PI * x) - 1) / 2;
  return b + factor * delta;
}

Exponential: 指数曲线的缓动(2^t);

基于 2 的幂次的缓动,起步 / 结束极慢,中间阶段变化极快,具有强烈的速度反差。

// 指数淡入(从极慢到极快)
function expoEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = Math.pow(2, 10 * (x - 1));
  return b + factor * delta;
}

// 指数淡出(从极快到极慢)
function expoEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = 1 - Math.pow(2, -10 * x);
  return b + factor * delta;
}

// 指数淡入淡出
function expoEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  let factor;
  if (x < 0.5) {
    factor = Math.pow(2, 20 * x - 10) / 2;
  } else {
    factor = (2 - Math.pow(2, -20 * x + 10)) / 2;
  }
  return b + factor * delta;
}

Circular: 圆形曲线的缓动(sqrt(1-t^2));

基于圆的平方根(√(1-t²))的缓动,变化曲线比幂次缓动更具张力。

// 圆形淡入
function circEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = 1 - Math.sqrt(1 - x * x);
  return b + factor * delta;
}

// 圆形淡出
function circEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  const factor = Math.sqrt(1 - (x - 1) * (x - 1));
  return b + factor * delta;
}

// 圆形淡入淡出
function circEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  let factor;
  if (x < 0.5) {
    factor = (1 - Math.sqrt(1 - 4 * x * x)) / 2;
  } else {
    factor = (Math.sqrt(1 - 4 * (x - 1) * (x - 1)) + 1) / 2;
  }
  return b + factor * delta;
}

Elastic: 指数衰减的正弦曲线缓动;

模拟弹簧的弹性效果,会超出目标值后回弹,包含 ** 振幅(a)和周期(p)** 两个核心参数,实现不同的弹性强度。

// 弹性淡入(开始前震荡)
function elasticEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  // 内部常量,独立定义不引用外部
  const p = d * 0.3; // 震荡周期
  let a = delta;    // 振幅
  const s = p / (2 * Math.PI) * Math.asin(Math.abs(delta) / a);
  const factor = -(a * Math.pow(2, 10 * (x - 1)) * Math.sin((x * d - s) * (2 * Math.PI) / p)) / delta;
  return b + factor * delta;
}

// 弹性淡出(结束后震荡)
function elasticEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  // 内部常量,独立定义不引用外部
  const p = d * 0.3; // 震荡周期
  let a = delta;    // 振幅
  const s = p / (2 * Math.PI) * Math.asin(Math.abs(delta) / a);
  const factor = (a * Math.pow(2, -10 * x) * Math.sin((x * d - s) * (2 * Math.PI) / p) + delta) / delta;
  return b + factor * delta;
}

// 弹性淡入淡出(开始和结束均震荡)
function elasticEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  // 内部常量,独立定义不引用外部
  const p = d * 0.3 * 1.5; // 调整震荡周期
  let a = delta;          // 振幅
  const s = p / (2 * Math.PI) * Math.asin(Math.abs(delta) / a);
  let factor;
  if (x < 0.5) {
    factor = -(a * Math.pow(2, 20 * x - 10) * Math.sin((20 * x * d - s) * (2 * Math.PI) / p)) / (2 * delta);
  } else {
    factor = (a * Math.pow(2, -20 * x + 10) * Math.sin((20 * x * d - 20 * d - s) * (2 * Math.PI) / p) + 2 * delta) / (2 * delta);
  }
  return b + factor * delta;
}

Back: 超过范围的三次方缓动((s+1)t^3 – st^2);

模拟物体拉回后再弹出的效果,会先向初始值的反方向移动(回退),再向目标值加速,通过 ** 回退系数(s)** 控制回退幅度。

// 回退淡入(开始前回退)
function backEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  // 回退系数,经典值1.70158,内部常量
  const s = 1.70158;
  const factor = x * x * ((s + 1) * x - s);
  return b + factor * delta;
}

// 回退淡出(结束前回退)
function backEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  // 回退系数,经典值1.70158,内部常量
  const s = 1.70158;
  const factor = (x - 1) * x * ((s + 1) * (x - 1) + s) + 1;
  return b + factor * delta;
}

// 回退淡入淡出(开始和结束前均回退)
function backEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  // 回退系数,淡入淡出专用(1.70158*1.525),内部常量
  const s = 2.592659;
  let factor;
  if (x < 0.5) {
    factor = 4 * x * x * ((s + 1) * 2 * x - s) / 2;
  } else {
    factor = (4 * (x - 1) * (x - 0.5) * ((s + 1) * (2 * x - 2) + s) + 2) / 2;
  }
  return b + factor * delta;
}

Bounce: 指数衰减的反弹缓动。

模拟物体落地的弹跳效果,多次反弹后逐渐停止,通过分段判断实现物理真实的弹跳次数和幅度。

// 弹跳淡入(开始前弹跳,独立实现无引用)
function bounceEaseIn(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  // 完全内联弹跳逻辑,不引用其他方法
  let bounceFactor;
  const xReverse = 1 - x;
  if (xReverse < 1 / 2.75) {
    bounceFactor = 7.5625 * xReverse * xReverse;
  } else if (xReverse < 2 / 2.75) {
    bounceFactor = 7.5625 * (xReverse - 1.5 / 2.75) * (xReverse - 1.5 / 2.75) + 0.75;
  } else if (xReverse < 2.5 / 2.75) {
    bounceFactor = 7.5625 * (xReverse - 2.25 / 2.75) * (xReverse - 2.25 / 2.75) + 0.9375;
  } else {
    bounceFactor = 7.5625 * (xReverse - 2.625 / 2.75) * (xReverse - 2.625 / 2.75) + 0.984375;
  }
  const factor = 1 - bounceFactor;
  return b + factor * delta;
}

// 弹跳淡出(结束后弹跳,核心弹跳效果)
function bounceEaseOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  // 弹跳核心公式,经典弹跳系数
  let factor;
  if (x < 1 / 2.75) {
    factor = 7.5625 * x * x;
  } else if (x < 2 / 2.75) {
    factor = 7.5625 * (x - 1.5 / 2.75) * (x - 1.5 / 2.75) + 0.75;
  } else if (x < 2.5 / 2.75) {
    factor = 7.5625 * (x - 2.25 / 2.75) * (x - 2.25 / 2.75) + 0.9375;
  } else {
    factor = 7.5625 * (x - 2.625 / 2.75) * (x - 2.625 / 2.75) + 0.984375;
  }
  return b + factor * delta;
}

// 弹跳淡入淡出(开始和结束均弹跳,独立实现无引用)
function bounceEaseInOut(t, b, c, d) {
  if (t <= 0) return b;
  if (t >= d) return c;
  const x = t / d;
  const delta = c - b;
  // 完全内联弹跳逻辑,不引用其他方法
  let factor;
  if (x < 0.5) {
    const x2 = 1 - 2 * x;
    let bounceFactor;
    if (x2 < 1 / 2.75) {
      bounceFactor = 7.5625 * x2 * x2;
    } else if (x2 < 2 / 2.75) {
      bounceFactor = 7.5625 * (x2 - 1.5 / 2.75) * (x2 - 1.5 / 2.75) + 0.75;
    } else if (x2 < 2.5 / 2.75) {
      bounceFactor = 7.5625 * (x2 - 2.25 / 2.75) * (x2 - 2.25 / 2.75) + 0.9375;
    } else {
      bounceFactor = 7.5625 * (x2 - 2.625 / 2.75) * (x2 - 2.625 / 2.75) + 0.984375;
    }
    factor = (1 - bounceFactor) / 2;
  } else {
    const x2 = 2 * x - 1;
    let bounceFactor;
    if (x2 < 1 / 2.75) {
      bounceFactor = 7.5625 * x2 * x2;
    } else if (x2 < 2 / 2.75) {
      bounceFactor = 7.5625 * (x2 - 1.5 / 2.75) * (x2 - 1.5 / 2.75) + 0.75;
    } else if (x2 < 2.5 / 2.75) {
      bounceFactor = 7.5625 * (x2 - 2.25 / 2.75) * (x2 - 2.25 / 2.75) + 0.9375;
    } else {
      bounceFactor = 7.5625 * (x2 - 2.625 / 2.75) * (x2 - 2.625 / 2.75) + 0.984375;
    }
    factor = (1 + bounceFactor) / 2;
  }
  return b + factor * delta;
}

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