《TypeScript圖形渲染實戰:2D架構設計與實現》 —3 動畫與Application類
第2篇
Canvas2D篇
(? 第3章? 動畫與Application類
(? 第4章? 使用Canvas2D繪圖
第3章? 動畫與Application類
本章將從程序實現的角度來了解一下動畫的原理,以及HTML 5提供的一些基礎且必要的方法。
可以將動畫的相關功能都封裝到一個名為Application的類中,該類主要是作為應用程序的入口類。它能啟動或關閉動畫循環,抽象更新與重繪流程,提供事件分發和處理功能,并且具有一個允許以不同幀率運行的計時器。
在開始本章內容前先聲明一下,本書所有的Demo以Chrome瀏覽器為主要測試環境,為簡單起見,本書并不關注各瀏覽器之間的兼容性。
3.1? requestAnimationFrame方法與動畫
從程序的角度來描述,筆者認為,動畫就是不間斷地、基于時間的更新與重繪。可以說這句話貫穿了本節要講的所有內容。
3.1.1? HTML中不間斷的循環
所謂不間斷的,是指動畫需要一個不停地重復循環機制,從程序實現的角度來說,一般有兩種選擇:
一種是類似于while ( true ) { }之類的死循環,除非滿足退出死循環的條件,否則就一直不停地重復相同的行為。在Windows下的D3D / OpenGL開發中,經常使用這種模式來驅動動畫不斷運行。作為知識的延伸點,下面來看一段經典的Windows下基于C / C++語言的動畫循環演示代碼。若不感興趣可直接跳過。具體代碼如下:
MSG msg ;
ZeroMemory ( & msg , sizeof ( msg ) ) ;
// 只有明確地收到WM_QUIT消息,才跳出while循環,退出應用程序
// 否則一直循環重復相同的行為
// Windows下經典的runLoop操作
while ( msg . message != WM_QUIT )
{
// 如果當前線程消息隊列中有消息,則取出該消息
if ( PeekMessage ( & msg , NULL , 0U , 0U , PM_REMOVE ) ) {
//將鍵盤的虛擬鍵消息轉換為WM_CHAR消息,并將WM_CHAR消息再次放入當前線程
消息隊列中,下次還是可以被PeekMessage讀取并處理
TranslateMessage ( & msg ) ;
//將當前的WM_開頭的消息分發到Window 窗口過程處理回調函數中進行處理
DispatchMessage ( & msg ) ;
//上面的代碼實際就是處理鼠標、鍵盤、WM_PAINT,或者計時器等隊列消息
} else {
//如果當前線程消息隊列中(上面的代碼處理消息隊列)沒有消息可處理,就一直更新并重繪
Update ( ) ; ?????????????????????????????????????????????? //更新
Render ( ) ; ?????????????????????????????????????????????? //重繪
}
}
另外一種是類似于定時器的回調,例如使用HTML DOM(Document Object Model,文檔對象模型)中Window對象的setTimeout、setInterval及requestAnimationFrame方法。關于setTimeout和setInterval的用法,請各位讀者自行查閱相關資料(在3.4節中將實現類似setTimeout和setInterval的功能)。下面主要來看一下requestAnimationFrame方法的用法。具體代碼如下:
// start記錄的是第一次調用step函數的時間點,用于計算與第一次調用step函數的時間差,
以毫秒為單位
let start : number = 0 ;
//lastTime記錄的是上一次調用step函數的時間點,用于計算兩幀之間的時間差,以毫秒為單位
let lastTime : number = 0 ;
// count用于記錄step函數運行的次數
let count : number = 0 ;
// step函數用于計算:
// 1.獲取當前時間點與HTML程序啟動時的時間差 : timestamp
// 2.獲取當前時間點與第一次調用step時的時間差 : elapsedMsec
// 3.獲取當前時間點與上一次調用step時的時間差 : intervalMsec
// step函數是作為requestAnimationFrame方法的回調函數使用的
// 因此step函數的簽名必須是 ( timestamp : number ) => void
function step ( timestamp : number ) : void? {
// 第一次調用本函數時,設置start和lastTime為timestamp
if ( ! start ) start = timestamp ;
if ( ! lastTime ) lastTime = timestamp ;
// 計算當前時間點與第一次調用step時間點的差
let elapsedMsec : number = timestamp - start ;
// 計算當前時間點與上一次調用step時間點的差(可以理解為兩幀之間的時間差)
let intervalMsec : number = timestamp - lastTime ;
// 記錄上一次的時間戳
lastTime = timestamp ;
// 計數器,用于記錄step函數被調用的次數
count ++ ;
console . log ( " " + count + " timestamp = " + timestamp ) ;
console . log ( " " + count + " elapsedMsec = " + elapsedMsec ) ;
console . log ( " " + count + " intervalMsec = " + intervalMsec) ;
// 使用requestAnimationFrame調用step函數
window . requestAnimationFrame ( step ) ;
}
// 使用requestAnimationFrame啟動step
// 而step函數中又會調用requestAnimationFrame來回調step函數
// 從而形成不間斷地遞歸調用,驅動動畫不停地運行
window . requestAnimationFrame ( step ) ;
上述代碼每次調用step函數會在瀏覽器的console控制臺窗口中輸出當前函數的調用次數,以及3個時間差的數值。Chrome瀏覽器中console控制臺窗口輸出的結果,如圖3.1所示。
圖3.1? Chrome瀏覽器中的requestAnimationFrame輸出時間差
渲染 架構設計 TypeScript
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。