掌控物體運動藝術:圖撲 Easing 函式實踐應用

hightopo發表於2024-10-30

現如今,前端開發除了構建功能性的網站和應用程式外,還需要建立具有吸引力且尤為流暢互動的使用者介面,其中動畫技術在其中發揮著至關重要的作用。在數字孿生領域,動畫的應用顯得尤為重要。數字孿生技術透過精確模擬現實世界中的物件、過程和系統,對動畫的需求遠遠超過傳統前端開發。

在這種環境中,動畫不僅僅是為了美觀,更是用於實現系統與現實的同步、演示覆雜過程和資料視覺化的關鍵手段。

HT 動畫介紹

在足夠短的時間內快速連續地改變物體的某個屬性,人的眼睛會感知到物體在平滑移動,這種利用人類視覺持續性產生的效果就是動畫。圖撲自研 HT for Web 產品中提供了多種建立動畫方式,其中很常見的是使用 ht.Default.startAnim 建立動畫

ht.Default.startAnim 支援兩種動畫模型:Frame-Based 和 Time-Based。這兩種型別的動畫所需的引數各不相同:

Frame-Based 幀動畫具有固定的幀數,即 action 被呼叫的次數,建立動畫時需傳入一下引數:

  • frames:動畫的幀數。
  • Interval:動畫幀間隔毫秒數。
  • easing:動畫緩動函式,預設為 ht.Default.animEasing。
  • finishFunc:動畫完成後的回撥函式。
  • action:必須提供 action 函式,用於實現動畫過程。第一個引數代表透過 easing 函式運算後的值,第二個引數代表當前動畫進度(0~1)。

Time-Based 週期動畫,動畫幀數(action 的呼叫次數)取決於系統環境,建立動畫需要傳入的引數:

  • duration:動畫週期的毫秒數,預設使用 ht.Default.animDuration。
  • easing:動畫的緩動函式,預設使用 ht.Default.animEasing。
  • finishFunc:動畫結束時的回撥函式。
  • action:必須提供 action 函式,用於實現動畫過程。


以小球落地過程為例,只需在動畫過程中不斷調整小球的位置屬性,就能實現小球落地的動畫效果:

const ball = dm.getDataByTag('ball'); // 獲取小球節點
const elevation = ball.getElevation(); // 獲取小球節點縱向位置
ht.Default.startAnim({
    duration: 1500, 
    easing: t => t,
    finishFunc: function () { }, 
    action: function (v, t) {
        ball.setElevation(elevation - elevation * v); // 在動畫中調整節點縱向位置
    }
});

在上圖中,小球的落地動畫效果已實現,但動畫看起來仍顯得有些生硬。這是因為在現實中,小球落地是加速運動的,並且當小球接觸地面後,受力變化會導致回彈。因此,我們還需要在動畫中控制小球的速度和運動趨勢,以便更真實地模擬這一過程。

那麼如何在動畫中控制速度呢?

這就需要引用下面的 easing 函式使用。

關於 Easing 函式

Easing(緩動函式)是用於調整動畫速度的函式,它們定義了動畫在開始、進行中和結束時的速度變化。這些函式允許動畫以非線性方式執行,使動畫效果更自然、流暢和有吸引力。緩動函式在座標軸中的表現可以看作是一個以時間(t)為橫軸、值為縱軸的圖表。以下附圖展示了一些常用的 easing 函式,從圖中可以清晰地看到不同 easing 的變化趨勢。

瞭解了 easing 函式的作用後,我們可以透過調整它來實現小球落地時的加速運動以及接觸地面後的回彈效果。

ht.Default.startAnim({
    duration: 1500,
    easing: function (t) {
        const n1 = 7.5625;
        const d1 = 2.75;
        if (t < 1 / d1) {
            return n1 * t * t;
        } else if (t < 2 / d1) {
            return n1 * (t -= 1.5 / d1) * t + 0.75;
        } else if (t < 2.5 / d1) {
            return n1 * (t -= 2.25 / d1) * t + 0.9375;
        } else {
            return n1 * (t -= 2.625 / d1) * t + 0.984375;
        }
    },
    finishFunc: function () { }, 
    action: function (v, t) {
        ball.setElevation(elevation - elevation * v);
    }
});

在實際專案中,物體的運動通常較為複雜,因此我們需要根據不同的運動型別選擇合適的 easing 函式。以下示例展示了在場景內的節點進行不同運動時,不同 easing 函式所產生的效果。

大家也可以該透過連結進行操作感受,透過切換不同的 easing 函式將呈現出不一樣的動畫效果:https://hightopo.com/demo/easing_animation_demo/

示例展示了多個動畫的連續播放效果。 我們可以在動畫的 finishFunc 回撥結束時,呼叫下一個動畫,從而實現連續的動畫效果。

ht.Default.startAnim({
    duration: 1500,
    easing: function (t) {    ......},
    action: function (v, t) { ...... },
    finishFunc: function () {
        // 呼叫下一個動畫        
        ht.Default.startAnim({            
            ......        
        })    
     }, 
});

總結

常聽人說 Easing 是動畫的靈魂,就如同生命的節奏。有些人厚積薄發,有些人平穩一生,而也有些人起起伏伏,經歷著高山低谷的跌宕起伏。無論過程如何多樣精彩,終點都是一致的——正如動畫中無論怎樣變化的 Easing 曲線,最終都通向同一個終點幀。動畫和人生一樣,豐富的過渡和變化,賦予它們獨特的美感和深意。

相關文章