tween.js 英文使用指南 首先來看個例子: hello,tween.js 補間(動畫)(來自 in between)是一個概念,允許你以平滑的方式更改對象的屬性。你只需告訴它哪些屬性要更改,當補間結束運行時它們應該具有哪些最終值,以及這需要多長時間,補間引擎將負責計算從起始點到結束點的值。 例 ...
tween.js 英文使用指南首先來看個例子: hello,tween.js
補間(動畫)(來自 in-between)是一個概念,允許你以平滑的方式更改對象的屬性。你只需告訴它哪些屬性要更改,當補間結束運行時它們應該具有哪些最終值,以及這需要多長時間,補間引擎將負責計算從起始點到結束點的值。
例如,position對象擁有x和y兩個坐標:
var position = { x: 100, y: 0 }
如果你想將x坐標的值從100變成200,你應該這麼做:
// 首先為位置創建一個補間(tween)
var tween = new TWEEN.Tween(position);
// 然後告訴 tween 我們想要在1000毫秒內以動畫的形式移動 x 的位置
tween.to({ x: 200 }, 1000);
一般來說這樣還不夠,tween 已經被創建了,但是它還沒被激活(使用),你需要這樣啟動:
// 啟動
tween.start();
最後,想要成功的完成這種效果,你需要在主函數中調用TWEEN.update,如下使用:
animate();
function animate() {
requestAnimationFrame(animate);
// [...]
TWEEN.update();
// [...]
}
這樣在更新每幀的時候都會運行補間動畫;經過 1秒後 (1000 毫秒) position.x將會變成 200。
除非你在控制臺中列印出 x 的值,不然你看不到它的變化。你可能想要使用 onUpdate 回調:
tween.onUpdate(function(object) {
console.log(object.x);
});
tips:你可能在這裡獲取不到 object.x ,具體的見我提的這個 issue
這個函數將會在動畫每次更新的時候被調用;這種情況發生的頻率取決於很多因素 - 例如,電腦或設備的速度有多快(以及如何繁忙)。
到目前為止,我們只使用補間動畫向控制台輸出值,但是您可以將它與 three.js 對象結合:
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回調。
你可能也註意到了一些不同的地方:tween.js 可以鏈式調用! 每個tween函數都會返回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 不會自行運行。你需要顯式的調用 update 方法來告訴它何時運行。推薦的方法是在主動畫迴圈中執行這個操作。使用 requestAnimationFrame 調用此迴圈以獲得最佳的圖形性能。
比如之前這個例子:
animate();
function animate() {
requestAnimationFrame(animate);
// [...]
TWEEN.update();
// [...]
}
如果調用的時候不傳入參數,update 將會判斷當前時間點以確定自上次運行以來已經有多久。
當然你也可以傳遞一個明確的時間參數給 update
TWEEN.update(100);
意思是"更新時間 = 100 毫秒"。你可以使用它來確保代碼中的所有時間相關函數都使用相同的時間值。例如,假設你有一個播放器,並希望同步運行補間。 你的 animate 函數可能看起來像這樣:
var currentTime = player.currentTime;
TWEEN.update(currentTime);
我們使用明確的時間值進行單元測試。你可以看下 tests.js 這個例子,看看我們如何用不同的值調用TWEEN.update() 來模擬時間傳遞。
控制一個補間
start 和 stop
到目前為止,我們已經瞭解了Tween.start方法,但是還有更多的方法來控制單個補間。 也許最重要的一個是 star 對應的方法:停止 。 如果你想取消一個補間,只要調用這個方法通過一個單獨的補間:
tween.stop();
停止一個從未開始或已經停止的補間沒有任何效果。 沒有錯誤被拋出。
start 方法接受一個參數 time。如果你使用它,那麼補間不會立即開始,直到特定時刻,否則會儘快啟動(i.e 即在下次調用 TWEEN.update)。
update
補間也有一個更新的方法---這實際上是由 TWEEN.update 調用的。 你通常不需要直接調用它,除非你是個 瘋狂的hacker。
chain
當你順序排列不同的補間時,事情會變得有趣,例如在上一個補間結束的時候立即啟動另外一個補間。我們稱這為鏈式補間,這使用 chain 方法去做。因此,為了使 tweenB 在 tewwnA 啟動:
tweenA.chain(tweenB);
或者,對於一個無限的鏈式,設置tweenA一旦tweenB完成就開始:
tweenA.chain(tweenB);
tweenB.chain(tweenA);
關於無限的鏈式查看 Hello world 。
在其他情況下,您可能需要將多個補間鏈接到另一個補間,以使它們(鏈接的補間)同時開始動畫:
tweenA.chain(tweenB,tweenC);
警告:調用 tweenA.chain(tweenB) 實際上修改了tweenA,所以tweenA總是在tweenA完成時啟動。 chain 的返回值只是tweenA,不是一個新的tween。
repeat
如果你想讓一個補間永遠重覆,你可以鏈接到自己,但更好的方法是使用 repeat 方法。 它接受一個參數,描述第一個補間完成後需要多少次重覆
tween.repeat(10); // 迴圈10次
tween.repeat(Infinity); // 無限迴圈
補間的總次數將是重覆參數加上一個初始補間。查看 Repeat。
yoyo
這個功能只有在獨自使用 repeat 時才有效果。 活躍時,補間的行為將像 yoyo 一樣,i.e 它會從起始值和結束值之間跳出,而不是從頭開始重覆相同的順序。
delay
更複雜的安排可能需要在實際開始運行之前延遲補間。 你可以使用 delay 方法來做到這一點
tween.delay(1000);
tween.start();
將在調用啟動方法後的1秒鐘後開始執行。
控制所有補間
在 TWEEN 全局對象中可以找到以下方法,除了 update 之外,通常不需要使用其中的大部分對象。
TWEEN.update(time)
我們已經討論過這種方法。 它用於更新所有活動的補間。
如果 time 不指定,它將使用當前時間。
TWEEN.getAll and TWEEN.removeAll
用於獲取對活動 tweens 數組的引用,並分別僅從一個調用中將它們全部從數組中刪除
TWEEN.add(tween) and TWEEN.remove(tween)
用於將補間添加到活動補間的列表,或者分別從列表中刪除特定的補間。
這些方法通常只在內部使用,但是如果您想要做一些有趣的事情,則會被暴露。
控制補間組
使用 TWEEN 單例來管理補間可能會導致包含許多組件的大型應用程式出現問題。 在這些情況下,您可能希望創建自己的更小的補間組。
示例:交叉組件衝突
如果使用 TWEEN 有多個組件,並且每個組件都想管理自己的一組補間,則可能發生衝突。 如果一個組件調用 TWEEN.update() 或 TWEEN.removeAll(),則其他組件的補間也將被更新或刪除。
創建你自己的補間組
為瞭解決這個問題,每個組件都可以創建自己的 TWEEN.Group 實例(這是全局的 TWEEN 對象在內部使用的)。 實例化新的補間時,可以將這些組作為第二個可選參數傳入:
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
通過這種方式,每個組件都可以處理創建,更新和銷毀自己的一組補間。
改變緩動功能
Tween.js 將以線性方式執行值之間的插值(即緩動),所以變化將與流逝的時間成正比。 這是可以預見的,但在視覺上也是相當無趣的。 不要擔心 - 使用緩動方法可以輕鬆更改此行為。 例如:
tween.easing(TWEEN.Easing.Quadratic.In);
這將導致緩慢地開始向最終值變化,向中間加速,然後迅速達到其最終值,相反,TWEEN.Easing.Quadratic.Out 一開始會加速,但隨著值的接近最終放緩。
可用的緩動函數:TWEEN.Easing
tween.js提供了一些現有的緩動功能。它們按照它們表示的方程式進行分組:線性,二次,三次,四次,五次,正弦,指數,圓形,彈性,背部和彈跳,然後是緩動型:In,Out和InOut。
除非您已經熟悉這些概念,否則這些名稱可能不會對您說什麼,所以您可能需要查看 Graphs 示例,該示例將一個頁面中的所有曲線進行圖形化,以便比較它們如何看待一瞥。
這些功能是從 Robert Penner 慷慨地提供幾年前作為自由軟體提供的原始方程派生而來的,但是已經被優化以便與JavaScript很好地發揮作用。
使用自定義緩動功能
您不僅可以使用任何現有的功能,還可以提供您自己的功能,只要遵循一些約定即可:
- 它必須接受一個參數:
k: 緩動過程,或我們的補間所處的時間有多長。允許的值在[0,1]的範圍內。 - 它必鬚根據輸入參數返回一個值。
不管要修改多少個屬性,easing函數在每次更新時只調用一次。 然後將結果與初始值以及這個值和最終值之間的差值(delta)一起使用,就像這個偽代碼一樣:
easedElapsed = easing(k);
for each property:
newPropertyValue = initialPropertyValue + propertyDelta * easedElapsed;
對於更註重性能表現的人來說:只有在補間上調用 start() 時才會計算增量值。
因此,讓我們假設您想使用一個緩解值的自定義緩動函數,但是將 Math.floor 應用於輸出,所以只返回整數部分,從而產生一種梯級輸出:
function tenStepEasing(k) {
return Math.floor(k * 10) / 10;
}
你可以通過簡單地調用它的緩動方法來使用它,就像我們之前看到的那樣:
tween.easing(tenStepEasing);
查看 graphs for custom easing functions 示例,以查看這個動作(還有一些用於生成步進函數的元編程)。
回調函數
另一個強大的特性是能夠在每個補間的生命周期的特定時間運行自己的功能。 當更改屬性不夠時,通常需要這樣做。
例如,假設你正在試圖給一些不能直接訪問屬性的對象設置動畫,但是需要你調用setter。 您可以使用 update 回調來讀取新的更新值,然後手動調用setters。 所有的回調函數都將補間對象作為唯一的參數。
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 );
});
或者想象一下,當一個補間開始時,你想播放聲音。你可以使用 start 回調:
var tween = new TWEEN.Tween(obj)
.to({ x: 100 })
.onStart(function() {
sound.play();
});
每個回調的範圍是補間對象--在這種情況下,是 obj。
onStart
在補間開始之前執行--i.e. 在計算之前。每個補間只能執行一次,i.e. 當通過 repeat() 重覆補間時,它將不會運行。
同步到其他事件或觸發您要在補間啟動時發生的操作是非常好的。
補間對象作為第一個參數傳入。
onStop
當通過 stop() 顯式停止補間時執行,但在正常完成時並且在停止任何可能的鏈補間之前執行補間。
補間對象作為第一個參數傳入。
onUpdate
每次補間更新時執行,實際更新後的值。
補間對象作為第一個參數傳入。
onComplete
當補間正常完成(即不停止)時執行。
補間對象作為第一個參數傳入。
高級補間
相對值
使用 to 方法時,也可以使用相對值。 當tween啟動時,Tween.js將讀取當前屬性值並應用相對值來找出新的最終值。
但是你需要使用引號,否則這些值將被視為絕對的。 我們來看一個例子:
// 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 示例。
補間值的數組
除了補間為絕對值或相對值之外,還可以讓Tween.js跨一系列值更改屬性。 要做到這一點,你只需要指定一個數組的值,而不是一個屬性的單個值。 例如:
var tween = new TWEEN.Tween(relativeObj).to({ x: [0, -100, 100] });
將使 x 從初始值變為0,-100和100。
這些值的計算方法如下:
- 首先,補間進度如常計算
- 進度(從0到1)用作插值函數的輸入
- 基於進度和值的數組,生成內插值
例如,當補間剛剛啟動(進度為0)時,插值函數將返回數組中的第一個值。 當補間到一半時,插值函數將返回一個大約在數組中間的值,當補間結束時,插值函數將返回最後一個值。
您可以使用插值方法更改插值函數。 例如:
tween.interpolation( TWEEN.Interpolation.Bezier );
以下值可用:
- TWEEN.Interpolation.Linear
- TWEEN.Interpolation.Bezier
- TWEEN.Interpolation.CatmullRom
預設是 Linear。
請註意,插值函數對於與同一補間中的數組進行補間的所有屬性是全局的。
您不能使用數組和線性函數進行屬性A的更改,也不能使用相同的補間進行數組B的屬性B和Bezier函數的更改; 您應該使用運行在同一對象上的兩個補間對象,但修改不同的屬性並使用不同的插值函數。
查看 06_array_interpolation 示例。
獲得最佳性能
雖然Tween.js試圖自己執行,但是沒有什麼能夠阻止你以一種反作用的方式使用它。 這裡有一些方法可以避免在使用Tween.js時(或者在網頁中進行動畫製作時)減慢項目速度。
使用高性能的CSS
當您嘗試在頁面中設置元素的位置時,最簡單的解決方案是為 top 和 left 屬性設置動畫,如下所示:
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';
});
但這實際上是效率低下的,因為改變這些屬性會迫使瀏覽器在每次更新時重新計算佈局,這是非常昂貴的操作。 相反的,您應該使用 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);';
});
如果你想瞭解更多關於這個,看看這篇文章。
但是,如果您的動畫需求非常簡單,那麼在適用的情況下使用CSS動畫或轉換可能會更好,以便瀏覽器儘可能優化。
當您的動畫需要涉及複雜的佈局時,Tween.js是非常有用的,也就是說,您需要將多個補間同步到一起,在完成一些動作之後,迴圈多次等等。
對垃圾收集器(別名GC)
如果你使用onUpdate回調函數,你需要非常小心的使用它。 因為這個函數每秒鐘會被調用很多次,所以如果每次更新都要花費很多的代價,那麼你可能會阻塞主線程並導致可怕的結果,或者如果你的操作涉及到記憶體分配的話, 垃圾收集器運行太頻繁,也導致結果。 所以只是不要做些事情中的其中一個。 保持你的onUpdate回調非常輕量級,並確保在開發時也使用記憶體分析器。
瘋狂的補間
這是你可能不經常使用的東西,但是你可以在Tween.js之外使用補間公式。 畢竟,它們只是功能。 所以你可以使用它們來計算平滑曲線作為輸入數據。
例如,他們用於在 這個實驗 中生成音頻數據。
https://github.com/zhaoqize/blog/issues/10