【动画】想怎么动就怎么动,在我的地盘你得听我的

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

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



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

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


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

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



html代码


<div id="motionPath"></div>


css代码


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


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


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


  1. paused:控制动画暂停标志


  1. duration:动画过渡时间


  1. easing:动画过渡曲线,配置常规的动画过渡曲线参考缓动函数


  1. 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() 函数的实现


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元素


  1. type:过渡的目标属性类型,元素的width归并于css


  1. property:目标过渡的属性名,也就是元素的宽width


  1. fromNumber:过渡的属性的起始值20px


  1. 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,相信有这样的思路必然需求也会迎刃而解


相关文章
|
2月前
|
机器学习/深度学习
阿里Animate Anyone:让任何静态图像动起来
【2月更文挑战第17天】阿里Animate Anyone:让任何静态图像动起来
248 3
阿里Animate Anyone:让任何静态图像动起来
|
6月前
|
小程序 JavaScript
小程序动画animation向左移动效果
小程序动画animation向左移动效果
41 0
|
9月前
|
JavaScript 前端开发
路由动画切换( 左移,右移)
路由动画切换( 左移,右移)
|
6月前
鼠标变小手
鼠标变小手
|
iOS开发
iOS开发 - 渐变导航条升级版(判断滚动的方向和改变方向时的位置)
iOS开发 - 渐变导航条升级版(判断滚动的方向和改变方向时的位置)
121 0
iOS开发 - 渐变导航条升级版(判断滚动的方向和改变方向时的位置)
|
前端开发
html+css鼠标经过的样式变化例题
当用户移动鼠标到指定的区域或内容时,会出现什么不一样的渲染效果呢?看这篇文章一起来了解吧!
112 0
 html+css鼠标经过的样式变化例题
|
数据采集 算法 数据挖掘
代码能跑就不要动,为什么我们都会有这样的想法?
代码能跑就不要动,为什么我们都会有这样的想法?
239 0
|
前端开发 JavaScript
使用konvajs三步实现一个小球游戏
使用konvajs三步实现一个小球游戏
250 0
使用konvajs三步实现一个小球游戏
右侧是长方形和半圆结合 光标放上去在规定时间内完成动画
右侧是长方形和半圆结合 光标放上去在规定时间内完成动画
|
数据可视化 5G 定位技术
折叠屏已经不新鲜了,明天你可能会用上“伸缩屏幕”!
尽管智能手机已经如此便捷灵活,却一直在竭力克服一个看上去最基本的问题:显示屏太小。诚然,一些电话已经做的比通常更大以提供更大的显示空间。但是如果手机做得过大以至于裤兜都无法容纳,这在很多人看来是不能接受的。
121 0
折叠屏已经不新鲜了,明天你可能会用上“伸缩屏幕”!