【动画】想怎么动就怎么动,在个人地盘你得听个人

transition、animation是CSS3中制做DOM元素动画的重要属性,但其仅仅只能应对一些常规的需求,针对非DOM元素的属性过渡时就显得无能为力了,因此接下来经过js简单实现其动画原理(说白了也就是经过JavaScript间接操做目标过渡属性值,每隔一段时间更新一次)让动画变得更加灵活可控,而不是对可恶的pm说这效果有啥用、砍掉这类的话,固然还得看需求使用场景css

先给个demo: 针对这样的需求怎么实现呢? html

纯css显然已没法作到(图片有点失真,在线众筹换mac, 意思到位了就行~)前端

简单概述下上面的gif,布局采用了d3实现,针对这种需求常规的可视化图形插件(如ECharts、ichartjs等),经过简单的配置可能已没法作到,就算能实现灵活度也必然受到了限制。熟悉d3的同窗实现起来也不会太复杂,图中主要是数字的变化以及弧形的颜色渐变的效果应如何实现,以及总体的联动。下面经过js来描述动画原理,使得更易于掌握css过渡transition及动画animationcss3

关于transition和animation属性介绍有不少文章参考一篇CSS3的动画属性

接下来就使用js简单实现一个让div元素的width属性从20px过渡到200px的动画效果

html代码git

<div id="motionPath"></div>
复制代码

css代码github

#motionPath {
  width: 20px;
  height: 100px;
  color:#333;
  background-color: red;
}
复制代码

在实现以前,先简单了解下贝塞尔曲线的原理参考贝塞尔曲线扫盲,了解了原理以后推荐一个实现三次贝塞尔曲线的js库CubicBezier,也是下文中使用的动画函数,等价CSS transition-timing-function、animation-timing-functionbash

首先定义过渡动画的几个参数:函数

  1. paused:控制动画暂停标志
  2. duration:动画过渡时间
  3. easing:动画过渡曲线,配置常规的动画过渡曲线参考缓动函数
  4. update:动画更新时的回调函数
var BezierEasing = require('bezier-easing')
var tween = {
  paused: false,
  duration: 6000,
  easing: BezierEasing(0, 0, 1, 0.5),
  update: function (v) {
    // anim 是下文定义的一个描述动画的对象
    anim.target.innerHTML = v
  }
}
复制代码

定义一个动画开始函数play()布局

var raf = null
function play() {
  raf = requestAnimationFrame(function (t) {
    step(t);
  })

  function step(t) {
    if (!tween.paused) {
      setInstanceProgress(t);
      play();
    } else {
      raf = cancelAnimationFrame(raf);
    }
  }
}
复制代码

step() 为动画的入口,这里使用requestAnimationFrame关键帧动画函数,固然也可用setTimeout来模拟实现,但更推荐使用requestAnimationFrame,推荐阅读深刻理解requestAnimationFrame。在每个动画关键帧周期内会调用setInstanceProgress() 函数,下面来看下setInstanceProgress() 函数的实现post

function setInstanceProgress(engineTime) {
  var insTime = engineTime;

  if (insTime > tween.duration) {
    tween.paused = true;
  }

  var currentTime = Math.min(Math.max(insTime, 0), tween.duration);
  
  setAnimationsProgress(currentTime);
}
复制代码

setInstanceProgress() 函数对动画时间进行了修正,同时会判断动画是否应该结束,并将修正的时间传给了setAnimationsProgress() 函数,一样再来看看setAnimationsProgress()函数的实现,咱们知道动画实际上是“位移”关于“时间”的函数:s=f(t)将动画函数与时间关联起来,计算 t 时刻动画属性值 f(t)

接下来再定义一个描述动画的对象 以下各参数的含义:

  1. target:过渡的目标对象,当前是div元素
  2. type:过渡的目标属性类型,元素的width归并于css
  3. property:目标过渡的属性名,也就是元素的宽width
  4. fromNumber:过渡的属性的起始值20px
  5. toNumber:过渡属性的结束值200px
var anim = {
  target: document.getElementById('motionPath'),
  type: 'css',
  property: 'width',
  fromNumber: 20,
  toNumber: 200
}
复制代码

setAnimationsProgress() 函数

function setAnimationsProgress(insTime) {
  var elapsed = insTime / tween.duration;

  var eased = tween.easing(elapsed);

  var value = anim.fromNumber + (eased * (anim.toNumber - anim.fromNumber))

  setProgressValue[anim.type](anim.target, anim.property, value);
  
  tween.update(value);
}
复制代码

setAnimationsProgress() 函数也就是动画的核心,把时间和过渡函数相结合,计算t时刻对应的value值,再把value值设置到过渡的目标对象属性中去,接下来就是setProgressValue的实现,针对DOM元素,设置width属性也就是设置style对应的属性

var setProgressValue = {
  css: function (t, p, v) {
    t.style[p] = v + 'px'
  },
  plainkey: function (t, p, v) {
    t[p] = v
  }
}
复制代码

最后只须要运行play()函数,一个简单的动画也就实现了,效果以下:

若是说咱们要过渡一个对象plain={key: 0}中的key属性值从 0 到 100 是否是修改一下anim参数就能够了,以下:

var plain = {key: 0}
var anim = {
  target: plain,
  type: 'plainkey',
  property: 'key',
  fromNumber: 0,
  toNumber: 200
}
复制代码

key值的变化我就不给效果图了,总之经过这样简单的配置就解决了css不能实现相似的需求了,文章开头所说的数字变化的效果就完美的给解决了

最后一个问题了,文章开头的扇形的颜色渐变效果又应该如何实现呢,其实也很简单扇形是经过多份小扇形拼接而成的,而后设置相应的颜色,那每一个小扇形应该设置什么样色来达到渐变的效果呢?

举例:从颜色#e8cf22 变化到 #48b532,应用上面所讲的过渡动画原理,#e8cf22对应的rgb格式为rgb(232, 207, 34),#48b532对应的rgb格式为rgb(72, 181, 50),那么分别对R、G、B各值按照对应的起始值和对应的终点值进行过渡(R:从232到72,G:从207到181,B:从34到50),过渡过程当中再次组合对应的rgb值就是其过渡对应的颜色值,这就是颜色的过渡渐变原理,下面使用animejs这个库来实现,原理是同样的

html代码

<div id="motionPath"></div>
复制代码

css代码

#motionPath {
  color: blue;
  height: 100px;
}
复制代码

js代码

var oDev = document.getElementById('motionPath')
anime({
  targets: '#motionPath',
  duration: 6000,
  backgroundColor: ['rgb(232, 207, 34)', 'rgb(72, 181, 50)']
  easing: 'easeInOutQuad',
  update: function (instance) {
    oDev.innerHTML = instance.animations[0].currentValue;
  }
});
复制代码

效果以下:

综上:了解了js如何实现动画以后,接下来看个使用css3 animation的例子,效果图就不粘出来了。 以下示例代码:

// div
<div class="animation"></div>

// css
.animation
{
  width: 100px;
  height: 100px;
  background: red;
  position: relative;
  animation: myfirst 4s;
  animation-timing-function: cubic-bezier(0.42,0,0.58,1);
}
@keyframes myfirst
{
  0%   {background:red; left:0px; top:0px;}
  25%  {background:yellow; left:200px; top:0px;}
  50%  {background:blue; left:200px; top:200px;}
  75%  {background:green; left:0px; top:200px;}
  100% {background:red; left:0px; top:0px;}
}

/*
    效果表现为:让一个长宽都是100px的div元素,在不一样的时间阶段,结合过渡函数cubic-bezier(0.42,0,0.58,1)来进行颜色和位置的过渡变化,
 那么我想问0%、25%、50%、75%、100%指什么呢,过渡函数cubic-bezier(0.42,0,0.58,1)是应用到整个4s的过渡周期仍是每一个阶段应用一次完整的过渡? 
 
 答案:n%指的是所占过渡时间duration的百分数,0%~25%也就是从0s到1s阶段,而后结合时间t关于过渡函数cubic-bezier(0.42,0,0.58,1)来计算相应的位置点值和颜色值
*/

复制代码

最后

上文中的实现过程,读者你若是使用过animejs 这个动画函数库,也view过源代码,你会发现我只是参考其实现讲了一个大概,具体该函数库中一系列优秀的功能感兴趣的同窗能够参考学习,很是值得推荐,目前GitHub上star已到达了30k+

在应对pm提出的一些需求中,只要清晰的知道要过渡那个具体属性,须要从某个起始状态变化到某个指定状态,而后以什么样的方式(过渡函数)来进行过渡,再结合过渡时间 duration,相信有这样的思路必然需求也会迎刃而解

关于咱们

快狗打车前端团队专一前端技术分享,按期推送高质量文章,欢迎关注点赞。

相关文章
相关标签/搜索