詳解tween.js 中文使用指南
補間(動畫)是一個概念,允許你以平滑的方式更改對象的屬性。你只需告訴它哪些屬性要更改,當(dāng)補間結(jié)束運行時它們應(yīng)該具有哪些最終值,以及這需要多長時間,補間引擎將負責(zé)計算從起始點到結(jié)束點的值。
例如,position對象擁有x和y兩個坐標(biāo):
var position = { x: 100, y: 0 }
如果你想將x坐標(biāo)的值從100變成200,你應(yīng)該這么做:
// 首先為位置創(chuàng)建一個補間(tween) var tween = new TWEEN.Tween(position); // 然后告訴 tween 我們想要在1000毫秒內(nèi)以動畫的形式移動 x 的位置 tween.to({ x: 200 }, 1000);
一般來說這樣還不夠,tween 已經(jīng)被創(chuàng)建了,但是它還沒被激活(使用),你需要這樣啟動:
// 啟動 tween.start();
最后,想要成功的完成這種效果,你需要在主函數(shù)中調(diào)用TWEEN.update,如下使用:
animate(); function animate() { requestAnimationFrame(animate); // [...] TWEEN.update(); // [...] }
這樣在更新每幀的時候都會運行補間動畫;經(jīng)過 1秒后 (1000 毫秒) position.x將會變成 200。
除非你在控制臺中打印出 x 的值,不然你看不到它的變化。你可能想要使用 onUpdate 回調(diào):
tween.onUpdate(function(object) { console.log(object.x); });
tips:你可能在這里獲取不到 object.x ,具體的見我提的這個 issue
這個函數(shù)將會在動畫每次更新的時候被調(diào)用;這種情況發(fā)生的頻率取決于很多因素 - 例如,計算機或設(shè)備的速度有多快(以及如何繁忙)。
到目前為止,我們只使用補間動畫向控制臺輸出值,但是您可以將它與 three.js 對象結(jié)合:
var tween = new TWEEN.Tween(cube.position) .to({ x: 100, y: 100, z: 100 }, 10000) .start(); animate(); function animate() { requestAnimationFrame(animate); TWEEN.update(); threeRenderer.render(scene, camera); }
在這種情況下,因為three.js渲染器將在渲染之前查看對象的位置,所以不需要使用明確的onUpdate回調(diào)。
你可能也注意到了一些不同的地方:tween.js 可以鏈?zhǔn)秸{(diào)用! 每個tween函數(shù)都會返回tween實例,所以你可以重寫下面的代碼:
var tween = new TWEEN.Tween(position); tween.to({ x: 200 }, 1000); tween.start();
改成這樣:
var tween = new TWEEN.Tween(position) .to({ x: 200 }, 1000) .start();
在將會看到很多例子,所以熟悉它是很好的!比如04-simplest 這個例子。
tween.js的動畫
Tween.js 不會自行運行。你需要顯式的調(diào)用 update 方法來告訴它何時運行。推薦的方法是在主動畫循環(huán)中執(zhí)行這個操作。使用 requestAnimationFrame 調(diào)用此循環(huán)以獲得最佳的圖形性能。
比如之前這個例子:
animate(); function animate() { requestAnimationFrame(animate); // [...] TWEEN.update(); // [...] }
如果調(diào)用的時候不傳入?yún)?shù),update 將會判斷當(dāng)前時間點以確定自上次運行以來已經(jīng)有多久。
當(dāng)然你也可以傳遞一個明確的時間參數(shù)給 update
TWEEN.update(100);
意思是"更新時間 = 100 毫秒"。你可以使用它來確保代碼中的所有時間相關(guān)函數(shù)都使用相同的時間值。例如,假設(shè)你有一個播放器,并希望同步運行補間。 你的 animate 函數(shù)可能看起來像這樣:
var currentTime = player.currentTime; TWEEN.update(currentTime);
我們使用明確的時間值進行單元測試。你可以看下tests.js 這個例子,看看我們?nèi)绾斡貌煌闹嫡{(diào)用TWEEN.update() 來模擬時間傳遞。
控制一個補間
start 和 stop
到目前為止,我們已經(jīng)了解了Tween.start方法,但是還有更多的方法來控制單個補間。 也許最重要的一個是 star 對應(yīng)的方法:停止 。 如果你想取消一個補間,只要調(diào)用這個方法通過一個單獨的補間:
tween.stop();
停止一個從未開始或已經(jīng)停止的補間沒有任何效果。 沒有錯誤被拋出。
start 方法接受一個參數(shù) time。如果你使用它,那么補間不會立即開始,直到特定時刻,否則會盡快啟動(i.e 即在下次調(diào)用 TWEEN.update)。
update
補間也有一個更新的方法---這實際上是由 TWEEN.update 調(diào)用的。 你通常不需要直接調(diào)用它,除非你是個 瘋狂的hacker。
chain
當(dāng)你順序排列不同的補間時,事情會變得有趣,例如在上一個補間結(jié)束的時候立即啟動另外一個補間。我們稱這為鏈?zhǔn)窖a間,這使用 chain 方法去做。因此,為了使 tweenB 在 tewwnA 啟動:
tweenA.chain(tweenB);
或者,對于一個無限的鏈?zhǔn)剑O(shè)置tweenA一旦tweenB完成就開始:
tweenA.chain(tweenB); tweenB.chain(tweenA);
關(guān)于無限的鏈?zhǔn)讲榭?a target="_blank" rel="external nofollow" >Hello world 。
在其他情況下,您可能需要將多個補間鏈接到另一個補間,以使它們(鏈接的補間)同時開始動畫:
tweenA.chain(tweenB,tweenC);
警告:調(diào)用 tweenA.chain(tweenB) 實際上修改了tweenA,所以tweenA總是在tweenA完成時啟動。 chain 的返回值只是tweenA,不是一個新的tween。
repeat
如果你想讓一個補間永遠重復(fù),你可以鏈接到自己,但更好的方法是使用 repeat 方法。 它接受一個參數(shù),描述第一個補間完成后需要多少次重復(fù)
tween.repeat(10); // 循環(huán)10次 tween.repeat(Infinity); // 無限循環(huán)
補間的總次數(shù)將是重復(fù)參數(shù)加上一個初始補間。查看 Repeat。
yoyo
這個功能只有在獨自使用 repeat 時才有效果。 活躍時,補間的行為將像 yoyo 一樣,i.e 它會從起始值和結(jié)束值之間跳出,而不是從頭開始重復(fù)相同的順序。
delay
更復(fù)雜的安排可能需要在實際開始運行之前延遲補間。 你可以使用 delay 方法來做到這一點
tween.delay(1000); tween.start();
將在調(diào)用啟動方法后的1秒鐘后開始執(zhí)行。
控制所有補間
在 TWEEN 全局對象中可以找到以下方法,除了 update 之外,通常不需要使用其中的大部分對象。
TWEEN.update(time)
我們已經(jīng)討論過這種方法。 它用于更新所有活動的補間。
如果 time 不指定,它將使用當(dāng)前時間。
TWEEN.getAll and TWEEN.removeAll
用于獲取對活動 tweens 數(shù)組的引用,并分別僅從一個調(diào)用中將它們?nèi)繌臄?shù)組中刪除
TWEEN.add(tween) and TWEEN.remove(tween)
用于將補間添加到活動補間的列表,或者分別從列表中刪除特定的補間。
這些方法通常只在內(nèi)部使用,但是如果您想要做一些有趣的事情,則會被暴露。
控制補間組
使用 TWEEN 單例來管理補間可能會導(dǎo)致包含許多組件的大型應(yīng)用程序出現(xiàn)問題。 在這些情況下,您可能希望創(chuàng)建自己的更小的補間組。
示例:交叉組件沖突
如果使用 TWEEN 有多個組件,并且每個組件都想管理自己的一組補間,則可能發(fā)生沖突。 如果一個組件調(diào)用 TWEEN.update() 或 TWEEN.removeAll(),則其他組件的補間也將被更新或刪除。
創(chuàng)建你自己的補間組
為了解決這個問題,每個組件都可以創(chuàng)建自己的 TWEEN.Group 實例(這是全局的 TWEEN 對象在內(nèi)部使用的)。 實例化新的補間時,可以將這些組作為第二個可選參數(shù)傳入:
var groupA = new TWEEN.Group(); var groupB = new TWEEN.Group(); var tweenA = new TWEEN.Tween({ x: 1 }, groupA) .to({ x: 10 }, 100) .start(); var tweenB = new TWEEN.Tween({ x: 1 }, groupB) .to({ x: 10 }, 100) .start(); var tweenC = new TWEEN.Tween({ x: 1 }) .to({ x: 10 }, 100) .start(); groupA.update(); // 只更新tweenA groupB.update(); // 只更新tweenB TWEEN.update(); // 只更新tweenC groupA.removeAll(); // 只移除tweenA groupB.removeAll(); // 只移除tweenB TWEEN.removeAll(); // 只移除tweenC
通過這種方式,每個組件都可以處理創(chuàng)建,更新和銷毀自己的一組補間。
改變緩動功能
Tween.js 將以線性方式執(zhí)行值之間的插值(即緩動),所以變化將與流逝的時間成正比。 這是可以預(yù)見的,但在視覺上也是相當(dāng)無趣的。 不要擔(dān)心 - 使用緩動方法可以輕松更改此行為。 例如:
tween.easing(TWEEN.Easing.Quadratic.In);
這將導(dǎo)致緩慢地開始向最終值變化,向中間加速,然后迅速達到其最終值,相反,TWEEN.Easing.Quadratic.Out 一開始會加速,但隨著值的接近最終放緩。
可用的緩動函數(shù):TWEEN.Easing
tween.js提供了一些現(xiàn)有的緩動功能。它們按照它們表示的方程式進行分組:線性,二次,三次,四次,五次,正弦,指數(shù),圓形,彈性,背部和彈跳,然后是緩動型:In,Out和InOut。
除非您已經(jīng)熟悉這些概念,否則這些名稱可能不會對您說什么,所以您可能需要查看 Graphs 示例,該示例將一個頁面中的所有曲線進行圖形化,以便比較它們?nèi)绾慰创黄场?/p>
這些功能是從 Robert Penner 慷慨地提供幾年前作為自由軟件提供的原始方程派生而來的,但是已經(jīng)被優(yōu)化以便與JavaScript很好地發(fā)揮作用。
使用自定義緩動功能
您不僅可以使用任何現(xiàn)有的功能,還可以提供您自己的功能,只要遵循一些約定即可:
它必須接受一個參數(shù):
k: 緩動過程,或我們的補間所處的時間有多長。允許的值在[0,1]的范圍內(nèi)。
它必須根據(jù)輸入?yún)?shù)返回一個值。
不管要修改多少個屬性,easing函數(shù)在每次更新時只調(diào)用一次。 然后將結(jié)果與初始值以及這個值和最終值之間的差值(delta)一起使用,就像這個偽代碼一樣:
easedElapsed = easing(k); for each property: newPropertyValue = initialPropertyValue + propertyDelta * easedElapsed;
對于更注重性能表現(xiàn)的人來說:只有在補間上調(diào)用 start() 時才會計算增量值。
因此,讓我們假設(shè)您想使用一個緩解值的自定義緩動函數(shù),但是將 Math.floor 應(yīng)用于輸出,所以只返回整數(shù)部分,從而產(chǎn)生一種梯級輸出:
function tenStepEasing(k) { return Math.floor(k * 10) / 10; }
你可以通過簡單地調(diào)用它的緩動方法來使用它,就像我們之前看到的那樣:
tween.easing(tenStepEasing);
查看 graphs for custom easing functions 示例,以查看這個動作(還有一些用于生成步進函數(shù)的元編程)。
回調(diào)函數(shù)
另一個強大的特性是能夠在每個補間的生命周期的特定時間運行自己的功能。 當(dāng)更改屬性不夠時,通常需要這樣做。
例如,假設(shè)你正在試圖給一些不能直接訪問屬性的對象設(shè)置動畫,但是需要你調(diào)用setter。 您可以使用 update 回調(diào)來讀取新的更新值,然后手動調(diào)用setters。 所有的回調(diào)函數(shù)都將補間對象作為唯一的參數(shù)。
var trickyObjTween = new TWEEN.Tween({ propertyA: trickyObj.getPropertyA(), propertyB: trickyObj.getPropertyB() }) .to({ propertyA: 100, propertyB: 200 }) .onUpdate(function(object) { object.setA( object.propertyA ); object.setB( object.propertyB ); });
或者想象一下,當(dāng)一個補間開始時,你想播放聲音。你可以使用 start 回調(diào):
var tween = new TWEEN.Tween(obj) .to({ x: 100 }) .onStart(function() { sound.play(); });
每個回調(diào)的范圍是補間對象--在這種情況下,是 obj。
onStart
在補間開始之前執(zhí)行--i.e. 在計算之前。每個補間只能執(zhí)行一次,i.e. 當(dāng)通過 repeat() 重復(fù)補間時,它將不會運行。
同步到其他事件或觸發(fā)您要在補間啟動時發(fā)生的操作是非常好的。
補間對象作為第一個參數(shù)傳入。
onStop
當(dāng)通過 stop() 顯式停止補間時執(zhí)行,但在正常完成時并且在停止任何可能的鏈補間之前執(zhí)行補間。
補間對象作為第一個參數(shù)傳入。
onUpdate
每次補間更新時執(zhí)行,實際更新后的值。
補間對象作為第一個參數(shù)傳入。
onComplete
當(dāng)補間正常完成(即不停止)時執(zhí)行。
補間對象作為第一個參數(shù)傳入。
高級補間
相對值
使用 to 方法時,也可以使用相對值。 當(dāng)tween啟動時,Tween.js將讀取當(dāng)前屬性值并應(yīng)用相對值來找出新的最終值。
但是你需要使用引號,否則這些值將被視為絕對的。 我們來看一個例子:
// This will make the `x` property be 100, always var absoluteTween = new TWEEN.Tween(absoluteObj).to({ x: 100 }); // Suppose absoluteObj.x is 0 now absoluteTween.start(); // Makes x go to 100 // Suppose absoluteObj.x is -100 now absoluteTween.start(); // Makes x go to 100 // In contrast... // This will make the `x` property be 100 units more, // relative to the actual value when it starts var relativeTween = new TWEEN.Tween(relativeObj).to({ x: "+100" }); // Suppose relativeObj.x is 0 now relativeTween.start(); // Makes x go to 0 +100 = 100 // Suppose relativeObj.x is -100 now relativeTween.start(); // Makes x go to -100 +100 = 0
查看09_relative_values 示例。
補間值的數(shù)組
除了補間為絕對值或相對值之外,還可以讓Tween.js跨一系列值更改屬性。 要做到這一點,你只需要指定一個數(shù)組的值,而不是一個屬性的單個值。 例如:
var tween = new TWEEN.Tween(relativeObj).to({ x: [0, -100, 100] });
將使 x 從初始值變?yōu)?,-100和100。
這些值的計算方法如下:
- 首先,補間進度如常計算
- 進度(從0到1)用作插值函數(shù)的輸入
- 基于進度和值的數(shù)組,生成內(nèi)插值
例如,當(dāng)補間剛剛啟動(進度為0)時,插值函數(shù)將返回數(shù)組中的第一個值。 當(dāng)補間到一半時,插值函數(shù)將返回一個大約在數(shù)組中間的值,當(dāng)補間結(jié)束時,插值函數(shù)將返回最后一個值。
您可以使用插值方法更改插值函數(shù)。 例如:
tween.interpolation( TWEEN.Interpolation.Bezier );
以下值可用:
- TWEEN.Interpolation.Linear
- TWEEN.Interpolation.Bezier
- TWEEN.Interpolation.CatmullRom
默認(rèn)是 Linear。
請注意,插值函數(shù)對于與同一補間中的數(shù)組進行補間的所有屬性是全局的。
您不能使用數(shù)組和線性函數(shù)進行屬性A的更改,也不能使用相同的補間進行數(shù)組B的屬性B和Bezier函數(shù)的更改; 您應(yīng)該使用運行在同一對象上的兩個補間對象,但修改不同的屬性并使用不同的插值函數(shù)。
查看 06_array_interpolation 示例。
獲得最佳性能
雖然Tween.js試圖自己執(zhí)行,但是沒有什么能夠阻止你以一種反作用的方式使用它。 這里有一些方法可以避免在使用Tween.js時(或者在網(wǎng)頁中進行動畫制作時)減慢項目速度。
使用高性能的CSS
當(dāng)您嘗試在頁面中設(shè)置元素的位置時,最簡單的解決方案是為 top 和 left 屬性設(shè)置動畫,如下所示:
var element = document.getElementById('myElement'); var tween = new TWEEN.Tween({ top: 0, left: 0 }) .to({ top: 100, left: 100 }, 1000) .onUpdate(function(object) { element.style.top = object.top + 'px'; element.style.left = object.left + 'px'; });
但這實際上是效率低下的,因為改變這些屬性會迫使瀏覽器在每次更新時重新計算布局,這是非常昂貴的操作。 相反的,您應(yīng)該使用 transform,這不會使布局無效,并且在可能的情況下也將被硬件加速,比如:
var element = document.getElementById('myElement'); var tween = new TWEEN.Tween({ top: 0, left: 0 }) .to({ top: 100, left: 100 }, 1000) .onUpdate(function(object) { element.style.transform = 'translate(' + object.left + 'px, ' + object.top + 'px);'; });
如果你想了解更多關(guān)于這個,看看這篇文章。
但是,如果您的動畫需求非常簡單,那么在適用的情況下使用CSS動畫或轉(zhuǎn)換可能會更好,以便瀏覽器盡可能優(yōu)化。
當(dāng)您的動畫需要涉及復(fù)雜的布局時,Tween.js是非常有用的,也就是說,您需要將多個補間同步到一起,在完成一些動作之后,循環(huán)多次等等。
對垃圾收集器(別名GC)
如果你使用onUpdate回調(diào)函數(shù),你需要非常小心的使用它。 因為這個函數(shù)每秒鐘會被調(diào)用很多次,所以如果每次更新都要花費很多的代價,那么你可能會阻塞主線程并導(dǎo)致可怕的結(jié)果,或者如果你的操作涉及到內(nèi)存分配的話, 垃圾收集器運行太頻繁,也導(dǎo)致結(jié)果。 所以只是不要做些事情中的其中一個。 保持你的onUpdate回調(diào)非常輕量級,并確保在開發(fā)時也使用內(nèi)存分析器。
瘋狂的補間
這是你可能不經(jīng)常使用的東西,但是你可以在Tween.js之外使用補間公式。 畢竟,它們只是功能。 所以你可以使用它們來計算平滑曲線作為輸入數(shù)據(jù)。
例如,他們用于在 這個實驗 中生成音頻數(shù)據(jù)。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
js使用html2canvas實現(xiàn)屏幕截取的示例代碼
這篇文章主要介紹了js使用html2canvas實現(xiàn)屏幕截取的示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08微信小程序?qū)崿F(xiàn)圖片上傳、刪除和預(yù)覽功能的方法
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)圖片上傳、刪除和預(yù)覽功能的方法,涉及微信小程序界面布局、事件響應(yīng)及圖片操作相關(guān)實現(xiàn)技巧,需要的朋友可以參考下2017-12-12使用BootStrap實現(xiàn)表格隔行變色及hover變色并在需要時出現(xiàn)滾動條
這篇文章主要介紹了使用BootStrap實現(xiàn)表格隔行變色及hover變色并在需要時出現(xiàn)滾動條效果,代碼簡單易懂,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-01-01