动态

详情 返回 返回

一步一步學習使用FireMonkey動畫(6) 用實例理解動畫的運行狀態 - 动态 详情

雖然在過去的幾節課中,已經詳細的介紹了不少的屬性和方法以及事件,本節來看一看動畫的執行狀態。

回顧一下前面的幾節內容,可以看到,每一個動畫都具有如下的屬性和方法,請仔細閲讀下面的屬性或事件的作用,在接下來的案例中將會使用到它們:

1. 動畫的屬性和事件

  • Duration:該屬性表示動畫的持續時間(以秒為單位)。它是一個浮點值,因此你可以指定動畫播放的任何時間長度,而不僅僅是整數值。

  • Enabled:嚴格來説,該屬性決定動畫是否可以啓動。然而,該屬性的一般理解與其在設計時使用相關。如果你希望動畫在加載後自動啓動,可以將 Enabled 設置為 True ;如果你在運行時將此屬性從 True 更改為 False ,這也將導致動畫在該時刻停止。

注意:請不要將 Enabled 與Running屬性(Running屬性只具有Public可見性,而 Enabled 是一個Publish屬性,可以在屬性編輯器上進行修改)混淆。Enabled決定了動畫是否可以通過Start進行啓動。

  • Running: 這是Public的,並不是Published的屬性,因此未列在對象檢查器窗口中。這個只讀屬性可用於判斷動畫是否正在運行。當動畫開始時,此屬性的值將為 True ,並保持此值直到動畫結束(這可能意味着整個持續時間,或者由於某種原因動畫在完成前被停止)。完成的動畫將設置 Running 為 False 。

  • Delay: 此屬性設置動畫執行前等待的時間(以秒為單位)。如果你將此屬性設置為 1 (秒)並調用 Start 方法,動畫將在 1 秒後執行。

  • Interpolation:此屬性將允許你選擇應用於此特定動畫的插值修正函數。

  • AnimationType:此屬性與插值相關,你可以將其視為其修飾符。在大多數情況下,你可以將其視為在起始值和最終值之間選擇插值強調的位置。此屬性的可用值為 In 、 Out 和 InOut 。

  • Inverse:你可以將此屬性設置為 True 來反轉動畫方向;具體來説,你實際上反轉的是動畫中的時間流(你可以通過在動畫的 OnProcess 事件處理程序中打印 NormalizedTime 值來輕鬆證明這一點)。這也意味着所有動畫都是可逆的。

  • Loop: 這個布爾屬性決定動畫是否應該在其開始後循環播放。將此屬性設置為 True 將導致動畫在從初始值到最終值的過渡完成後(或 Duration 到期時)不會結束。通常,設置此屬性也會對列表中的下一個屬性——即 AutoReverse ——產生一些影響,因為您可能不希望盲目地從相同的初始點重複到目標值的相同過渡。當將 Loop 設置為 True 時,默認情況下就會發生這種情況。

  • AutoReverse:正如我們所見,所有動畫都可以反轉,並且可以配置為循環。 AutoReverse 屬性將動畫設置為在第一次運行完成後自動反轉(通過切換 Inverse 屬性)。因此,如果你正在將值從 A 過渡到 B,一旦達到 B,相同的動畫(此時 Inverse 設置為 True )將反向運行;也就是説,從 B 過渡到 A。最終持續時間將遵循(最終的) Delay + Duration (A 到 B)+ Duration (B 到 A)的方案。請注意,在第二次迭代之前不會應用任何 Delay ;此功能通常與將 Loop 設置為 True 相關聯(儘管它與它並不嚴格相關,但仍然可以獨立工作)。

  • CurrentTime:這是一個只讀屬性,它將提供動畫的當前執行時間;如果動畫未運行,它將是 0。你可以將其視為一個在實際過渡開始時啓動的秒錶(因此 Delay 永遠不會包含在內)。當 Inverse 為 False 時,會看到這個值增加,如果它為 True ,這個值會減小(當這是由 AutoReverse 屬性的值引起時,也會發生這種情況)。

  • NormalizedTime:這是用於插值的時間, NormalizedTime 屬性讓您可以訪問當前時間的值,包括插值。如果在動畫運行時比較 CurrentTime 和 NormalizedTime ,將看到實際時間與用於插值的時間(使用線性插值時,將看到重合的值)之間的差異。

  • Pause:Pause 屬性可以在其值為 True 時暫停動畫的執行;可以在動畫運行時(或之前)設置此屬性。

  • OnProcess事件:可以設置 OnProcess 事件的處理器,以便每次動畫將要計算新步驟時收到通知;在正常情況下,事件會頻繁的觸發。動畫的默認幀率為 60,全局動畫計時器的間隔也相應設置,因此可以預期每秒調用此事件 60 次。這是一個監控動畫的 CurrentTime 或 NormalizedTime 屬性值並使用它們進行某些附加目的(同步、日誌記錄等)的好地方。

  • OnFinish事件:當動畫完成所有中間步驟,並且當前過渡值與動畫設置的最終期望值一致時, OnFinish 事件就會被觸發。

請記住, OnFinish 事件只會發生一次。這意味着如果在同一執行過程中暫停動畫——即使多次暫停—— OnFinish 事件也不會被觸發。同樣適用於 AutoInverse 屬性的使用,當其設置為 True 時,基本上會導致動畫運行兩次,但不會導致 OnFinish 事件觸發兩次。

2. 使用示例觀察動畫的執行狀態

在這一節中,創建了一個非常簡單的例子,它包含一個使小球旋轉的TFloatAnimation組件,它的屬性設置如下所示:

img

  • AutoReverse為True,表示動畫會反向進行一次。
  • Enabled為False,表示動畫不會在啓動後立即執行。
  • Deploy為1,表示延遲1秒,Duration為5表示5秒跑完。

整個用户界面佈局如下所示:

img

在主窗體上添加了5個按鈕,一個TMemo和一個TProgressBar控件,添加了一個TTimer控件用來計時。

我們分別為TFloatAnimation的OnProcess和OnFinish添加了事件,並且在TTimer的OnTimer事件中進行全局的計時,代碼如下:

// FloatAnimation1動畫完成事件處理程序
procedure TfrmMain.FloatAnimation1Finish(Sender: TObject);
begin
  // 在Memo1控件中添加一行日誌,記錄動畫完成的時間
  Memo1.Lines.Add(TimeToStr(Now) + ': ' + '動畫已完成');

  // 重置開始時間標記,0通常表示未開始或已結束
  FStartedAt:=0;
end;

// FloatAnimation1動畫處理過程事件(每幀觸發)
procedure TfrmMain.FloatAnimation1Process(Sender: TObject);
// 聲明局部變量
 var
  // t: 用於存儲標準化時間進度(0.0到1.0)
  t,
  // Scale: 聲明但未使用的變量,可能用於縮放計算
  Scale: Single;
begin
  // 在Label3上顯示動畫的當前時間和標準化時間
  // FormatFloat將浮點數格式化為指定格式的字符串(保留3位小數)
  Label3.Text :=
    // 顯示動畫從開始到現在經過的時間(秒)
    '當前時間: ' + FormatFloat('0.000', FloatAnimation1.CurrentTime) +
    // 顯示動畫的標準化進度(0.0到1.0)
    '插值時間: ' + FormatFloat('0.000', FloatAnimation1.NormalizedTime);

  // 獲取當前的標準化時間進度(0.0表示開始,1.0表示結束)
  t := FloatAnimation1.NormalizedTime;

  // 在Label1上顯示動畫進度百分比
  // Format函數將浮點數格式化為百分比字符串(保留1位小數)
  Label1.Text := Format('進度: %.1f%%', [t * 100]);

  // 設置進度條的值,將標準化時間轉換為百分比(0-100)
  ProgressBar1.Value:=t*100;

end;

// 定時器Timer1的定時事件處理程序(按Interval間隔定期觸發)
procedure TfrmMain.Timer1Timer(Sender: TObject);
begin
  // 聲明並初始化一個空字符串,用於構建狀態信息
  var LStr := '';

  // 檢查動畫是否啓用
  if FloatAnimation1.Enabled then
    // 如果啓用,在狀態字符串後添加"-Enabled"標記
    LStr := LStr + '-Enabled';

  // 檢查動畫是否正在運行
  if FloatAnimation1.Running then
    // 如果正在運行,在狀態字符串後添加"-Running"標記
    LStr := LStr + '-Running';

  // 檢查動畫是否處於反向播放模式
  if FloatAnimation1.Inverse then
    // 如果處於反向模式,在狀態字符串後添加"-Inverse"標記
    LStr := LStr + '-Inverse';

  // 檢查動畫是否有有效的開始時間(FStartedAt > 0)
  if FStartedAt>0 then
    // 在Label2上顯示動畫狀態和從開始到現在經過的毫秒數
    // MilliSecondsBetween計算兩個時間點之間的毫秒數差
    // ToString將數值轉換為字符串
    Label2.Text := LStr + ' ' + MilliSecondsBetween(FStartedAt, Now).ToString + 'ms';
end;

接下來測試按下不同的按鈕,會發生什麼。

2.1 當單擊“啓動動畫”按鈕,

它會調用FloatAnimation.Start,並給FStartedAt賦當前時間值:

procedure TfrmMain.btnStartAnimationClick(Sender: TObject);
begin
  // 啓動FloatAnimation1動畫,開始播放
  FloatAnimation1.Start;
  // 記錄動畫開始的時間點,Now函數返回當前日期和時間
  FStartedAt := Now;
end;

運行時可以很清楚的看到運行狀態,直到出現“動畫已完成”的提示:

img

當調用FloatAnimation.Start後,TTimer開始顯示FloatAnimation的狀態,可以看到Enabled、Running和Inverse三個狀態分別為True,並且在延時1秒之後,CurrentTime和NormalizedTime才開始顯示值,進度條開始動起來。

FloatAnimation使用的是Quadratic插值算法,可以看到CurrentTime和NormalizedTime的值其實並不重合。

FloatAnimation的OnFinish事件直到反向動畫完成,實際上經歷了2個5秒,再加上延遲的1秒,因此最終經歷了差不多11秒時長。

2.2 如果按下"暫停/啓動動畫"按鈕,會出現什麼效果?

img
在啓動動畫後,按下暫停鍵,pause屬性設置為True後,動畫仍然處於Running狀態,所以無法單擊“啓動動畫”按鈕讓動畫重新運作。但是可以單擊“停止動畫”按鈕,讓動畫處於停止狀態,停止之後,CurrentTime和NormalizedTime歸0。

雖然動畫已經停止,但是Pause仍然為False狀態,所以在下次啓動動畫時,需要將Pause設置為True,動畫才能重新啓動,這跟很多播放器完全不相同

特別注意:暫停並不會觸發OnFinish事件,無論暫停多少次。

按鈕的事件處理代碼如下所示:

// 暫停/繼續動畫按鈕的點擊事件處理程序
procedure TfrmMain.btnPauseAnimationClick(Sender: TObject);
begin
  // 切換動畫的暫停狀態:如果當前是暫停則繼續,如果是播放則暫停
  FloatAnimation1.Pause := not FloatAnimation1.Pause;

  // 在Memo1控件中添加一行日誌,記錄當前時間和暫停狀態
  // TimeToStr(Now)將當前時間轉換為字符串格式
  // BoolToStr將布爾值轉換為字符串,True參數表示返回'True'/'False'而不是'-1'/'0'
  Memo1.Lines.Add(TimeToStr(Now) + ': ' + '暫停狀態 = ' + BoolToStr(FloatAnimation1.Pause, True));
end;

2.3 如果按下“啓用/禁用動畫”按鈕,會出現什麼效果?

“啓用/禁用動畫”按鈕會設置動畫的Enable屬性,如果設置Enable屬性為True,則如同調用Start方法,動畫立即開始運行。如果Enable為False,則動畫立即停止,並觸發OnFinish事件。

代碼如下所示:

// 啓用/禁用動畫按鈕的點擊事件處理程序
procedure TfrmMain.btnEnableDisableAnimationClick(Sender: TObject);
begin
  // 切換動畫的啓用狀態:如果當前啓用則禁用,如果禁用則啓用
  FloatAnimation1.Enabled := not FloatAnimation1.Enabled;

  // 在Memo1控件中添加一行日誌,記錄當前時間和啓用狀態
  Memo1.Lines.Add(TimeToStr(Now) + ': ' + 'Enabled 狀態 = ' + BoolToStr(FloatAnimation1.Enabled, True));

  // 如果動畫被啓用
  if FloatAnimation1.Enabled then
      // 重新記錄開始時間
      FStartedAt := Now;
end;

效果如下所示:

img

可以看到,禁用時,OnFinish被觸發,CurrentTime和NormalizedTime的時間被重置。

2.4 如果按下“停止動畫”按鈕,會出現什麼效果?

“停止動畫”按鈕調用Stop方法,代碼如下:

// 停止動畫按鈕的點擊事件處理程序
procedure TfrmMain.btnStopAnimationClick(Sender: TObject);
begin
  // 檢查動畫是否正在運行中
  if FloatAnimation1.Running then
     // 如果正在運行,則停止動畫
     FloatAnimation1.Stop;
end;

運行效果如下所示:

img

它與調用Enable=False相似,動畫立即停止,並重置了顯示時間。

2.5 如果按下“在當下位置停止”按鈕,會出現什麼效果?

這個按鈕的事件處理代碼如下:

// 停止在當前狀態按鈕的點擊事件處理程序
procedure TfrmMain.btnStopAtAnimationClick(Sender: TObject);
begin
  // 檢查動畫是否正在運行中
  if FloatAnimation1.Running then
     // 如果正在運行,則停止動畫並保持當前屬性值
     FloatAnimation1.StopAtCurrent;
end;

效果如下所示:

img

可以看到,StopAtCurrent方法,並不會重置時間,它保留了CurrentTime和NormalizedTime的值,但動畫確實已經停止,OnFinish已經觸發。

總結

當設計多個動畫聯合時,有時候要獲取或設置動畫的狀態,或者是要獲取動畫的執行時間來進行下一步的處理,這個時候瞭解動畫的執行狀態就特別有必要。
這節通過一個案例,詳細介紹了不同的動畫屬性在不同的狀態下的值,相信對於初學者,會有一定的啓發作用。

Add a new 评论

Some HTML is okay.