雖然在過去的幾節課中,已經詳細的介紹了不少的屬性和方法以及事件,本節來看一看動畫的執行狀態。
回顧一下前面的幾節內容,可以看到,每一個動畫都具有如下的屬性和方法,請仔細閲讀下面的屬性或事件的作用,在接下來的案例中將會使用到它們:
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組件,它的屬性設置如下所示:

- AutoReverse為True,表示動畫會反向進行一次。
- Enabled為False,表示動畫不會在啓動後立即執行。
- Deploy為1,表示延遲1秒,Duration為5表示5秒跑完。
整個用户界面佈局如下所示:

在主窗體上添加了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;
運行時可以很清楚的看到運行狀態,直到出現“動畫已完成”的提示:

當調用FloatAnimation.Start後,TTimer開始顯示FloatAnimation的狀態,可以看到Enabled、Running和Inverse三個狀態分別為True,並且在延時1秒之後,CurrentTime和NormalizedTime才開始顯示值,進度條開始動起來。
FloatAnimation使用的是Quadratic插值算法,可以看到CurrentTime和NormalizedTime的值其實並不重合。
FloatAnimation的OnFinish事件直到反向動畫完成,實際上經歷了2個5秒,再加上延遲的1秒,因此最終經歷了差不多11秒時長。
2.2 如果按下"暫停/啓動動畫"按鈕,會出現什麼效果?
在啓動動畫後,按下暫停鍵,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;
效果如下所示:

可以看到,禁用時,OnFinish被觸發,CurrentTime和NormalizedTime的時間被重置。
2.4 如果按下“停止動畫”按鈕,會出現什麼效果?
“停止動畫”按鈕調用Stop方法,代碼如下:
// 停止動畫按鈕的點擊事件處理程序
procedure TfrmMain.btnStopAnimationClick(Sender: TObject);
begin
// 檢查動畫是否正在運行中
if FloatAnimation1.Running then
// 如果正在運行,則停止動畫
FloatAnimation1.Stop;
end;
運行效果如下所示:

它與調用Enable=False相似,動畫立即停止,並重置了顯示時間。
2.5 如果按下“在當下位置停止”按鈕,會出現什麼效果?
這個按鈕的事件處理代碼如下:
// 停止在當前狀態按鈕的點擊事件處理程序
procedure TfrmMain.btnStopAtAnimationClick(Sender: TObject);
begin
// 檢查動畫是否正在運行中
if FloatAnimation1.Running then
// 如果正在運行,則停止動畫並保持當前屬性值
FloatAnimation1.StopAtCurrent;
end;
效果如下所示:

可以看到,StopAtCurrent方法,並不會重置時間,它保留了CurrentTime和NormalizedTime的值,但動畫確實已經停止,OnFinish已經觸發。
總結
當設計多個動畫聯合時,有時候要獲取或設置動畫的狀態,或者是要獲取動畫的執行時間來進行下一步的處理,這個時候瞭解動畫的執行狀態就特別有必要。
這節通過一個案例,詳細介紹了不同的動畫屬性在不同的狀態下的值,相信對於初學者,會有一定的啓發作用。