transition、animation是CSS3中製做DOM元素動畫的重要屬性,但其僅僅只能應對一些常規的需求,針對非DOM元素的屬性過渡時就顯得無能爲力了,因此接下來經過js簡單實現其動畫原理(說白了也就是經過JavaScript間接操做目標過渡屬性值,每隔一段時間更新一次)讓動畫變得更加靈活可控,而不是對可惡的pm說這效果有啥用、砍掉這類的話,固然還得看需求使用場景css
先給個demo: 針對這樣的需求怎麼實現呢? html
純css顯然已沒法作到(圖片有點失真,在線衆籌換mac, 意思到位了就行~)前端
簡單概述下上面的gif,佈局採用了d3實現,針對這種需求常規的可視化圖形插件(如ECharts、ichartjs等),經過簡單的配置可能已沒法作到,就算能實現靈活度也必然受到了限制。熟悉d3的同窗實現起來也不會太複雜,圖中主要是數字的變化以及弧形的顏色漸變的效果應如何實現,以及總體的聯動。下面經過js來描述動畫原理,使得更易於掌握css過渡transition及動畫animationcss3
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
首先定義過渡動畫的幾個參數:函數
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)
接下來再定義一個描述動畫的對象 以下各參數的含義:
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
,相信有這樣的思路必然需求也會迎刃而解
快狗打車前端團隊專一前端技術分享,按期推送高質量文章,歡迎關注點贊。