iT邦幫忙

第 12 屆 iThome 鐵人賽

0
Modern Web

再談 PixiJS,那些先前不一定有提到的部分與地雷系列 第 38

[Re:PixiJS - Day38] GSAP / TweenMax 常發生的補間動態覆蓋問題

這篇整理一些先前使用 GSAP 時遇到的問題


補間動態時觸發了一個補間動態:

  • 準備了一段動態: 兔子的 x0,兔子的 x1秒 中移動到 100
bunny.x = 0;
gsap.to(bunny, {duration: 1, x: 100});

問題: 當執行到一半時再觸發一次補間,並且 目標值改變 (通常是再次觸發了事件)。此時的結果為?
選項1: 堆疊 - 跑完原本 1秒 x 到 100的動態,再跑新增的 1秒 x 到 -100的動態;總時間為 2
選項2: 覆寫 - 從 0.5秒開始跑 1秒 x 到 -100的動態;總時間為 1.5

gsap.to(bunny, {duration: 1, x: 100});

setTimeout(()=>{
  gsap.to(bunny, {duration: 1, x: -100});	
}, 500);

答案: 2,直接覆寫,結束時間為覆寫時設定的時間,不會累加


這樣會有什麼問題?

調整: 覆寫動態與一開始的動態完全相同
結果: 覆寫動態觸發時, x 當下的值是 90,速度會 突然變很慢

  • 0秒 ~ 0.9 秒: 速度為 10/s,(x0 漸變到 90)
  • 0.9秒 ~ 1.9 秒: 速度為 1/s,(x90 漸變到 100)

[ Demo1 ]

bunny.x = 0;
gsap.to(bunny, {duration: 1, x: 100});

setTimeout(()=>{
  gsap.to(bunny, {duration: 1, x: 100});	
}, 900);

可行的解決方法:

  • 搭配 onStartonComplete 事件,
    既然是相同的段補間動畫,就讓整段補間動畫在執行時無法再被觸發
gsap.to(bunny, {
  duration: 1,
  x: 100,
  ease:"none",
  onStart:()=>{
    console.log("Start");
  },
  onComplete:()=>{
    console.log("Complete");
  }
});
  • 在兔子裡加上一個是否正在移動的變數
bunny.isTweening = false;

function tweenBunny(){
  // 如果兔子正在移動,就不接著繼續的補間動態
  if (bunny.isTweening === true) {return};
  gsap.to(bunny, 
  {
    duration: 1,
    x: Math.random() * 400,
    y: Math.random() * 400,
    onStart:()=>{
      bunny.isTweening = true;
    },
    onComplete:()=>{
      bunny.isTweening = false;
    }
  });
};

// 按鈕按下時執行 tweenBunny
btn.on("pointerdown", ()=>{
  tweenBunny();
});

[ Demo2 ]

  • 兔子停止時,按鈕為橘色可以點擊
  • 兔子移動時,按鈕為白色無法點擊

這是其中一種可行也方便做的方法,還有許多情境與作法,目前先不討論

若是希望新增的動態可以堆疊在原本的動態之後,可使用 Timeline


另一個類似且常見的問題:

一樣的動態:
準備: 兔子的 x0,兔子的 x1秒 中移動到 100

bunny.x = 0;
gsap.to(bunny, {duration: 1, x: 100});

問題:0.5 秒的時後,突然指定兔子的 x 屬性,這時的動態會如何?
選項1: 停止補間動態,兔子停在 x: -100 位置
選項2: 兔子在 x: -100一瞬間,然後繼續跑

// 這邊特別不使用 gsap 內建的方法以比較結果
setTimeout(()=>{
  bunny.x = -100;
}, 500);

答案: 2,並不會覆寫原本的補間動態


如何避免?

gsap.to() 指定的動態,就用 gsap.killTweensOf() 來停止

gsap.to(bunny, {duration: 1, x: 100, y:100, ease:"none", onComplete: function(){
  console.log('onComplete');
}});

setTimeout(()=>{
  // 直接停掉 gsap.to(bunny, {...}) 所有行為,且不觸發 onComplete 
  gsap.killTweensOf(bunny);
  gsap.set(bunny, {x: -100});
}, 500);
setTimeout(()=>{
  // 停掉 gsap.to(bunny, {x: 100}) 的補間動畫,
  // 由於還有 y 補間動畫,會觸發 onComplete 
  gsap.killTweensOf(bunny, {x: true}); 
  gsap.set(bunny, {x: -100});
}, 500);

以前的版本怎麼做:

版本: gsap 1.20.3
直接用 TweenMax.set() 就會停止原本的補間動畫

TweenMax.to(bunny, 1, {x: 100, y: 100, onComplete: function(){
  console.log("onComplete")
}});

setTimeout(()=>{
  // x 與 y 皆覆寫,不會觸發 onComplete
  TweenMax.set(bunny, {x: -100, y:0 });
}, 500);
setTimeout(()=>{
  // 只覆寫 x,y 的補間動態保留,仍會觸發 onComplete
  TweenMax.set(bunny, {x: -100 });
}, 500);

今日心得:

  • 舊版 TweenMax 就很好用也夠用,我持續使用了滿久的時間
  • gsap3 改版新增了很多有趣的功能,趁這個機會更新
  • scrolltrigger 這個 plugin 非常有趣,一直想搭配 PixiJS 做個什麼

其他參考資料:


上一篇
[Re:PixiJS - Day37] Pixi.js 開發工具的小趣事
下一篇
[Re:PixiJS - Day39] GSAP 的 PixiPlugin 介紹
系列文
再談 PixiJS,那些先前不一定有提到的部分與地雷45
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言