【動畫】想怎麼動就怎麼動,在個人地盤你得聽個人

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,相信有這樣的思路必然需求也會迎刃而解

關於咱們

快狗打車前端團隊專一前端技術分享,按期推送高質量文章,歡迎關注點贊。

相關文章
相關標籤/搜索