Java 實現動畫的基本原理統一:在屏幕上高頻繁繪製一系列幀,利用視覺暫留效應產生運動感覺。關鍵在於如何控制幀的產生節奏。
錯誤示範: 將動畫循環寫在 paint() 中 → 佔用 AWT 事件分發線程(EDT),導致界面卡死、無法響應用戶交互。
正確做法: 創建獨立線程運行動畫循環,定期調用 repaint() 請求重繪。
public class Animator1 extends java.applet.Applet implements Runnable {
int frame; // 當前幀號
int delay; // 幀間隔(毫秒)
Thread animator; // 動畫線程
public void init() {
String str = getParameter("fps");
int fps = (str != null) ? Integer.parseInt(str) : 10;
delay = (fps > 0) ? (1000 / fps) : 100; // 默認 100ms = 10fps
}
public void start() {
animator = new Thread(this);
animator.start();
}
public void run() {
// 關鍵:只有當前線程仍是 animator 時才繼續循環
while (Thread.currentThread() == animator) {
repaint(); // 請求 AWT 重繪
try {
Thread.sleep(delay); // 固定間隔休眠
} catch (InterruptedException e) {
break; // 被中斷則退出循環
}
frame++;
}
}
public void stop() {
animator = null; // 釋放線程引用,run() 循環條件失效,線程自然結束
}
}
HTML 引用:
<applet code=Animator1.class width=200 height=200>
<param name=fps value=20> <!-- 目標 20 FPS -->
</applet>
Thread.sleep(delay) 只保證「至少睡 delay 毫秒」,但不包含繪圖邏輯本身的耗時。若繪圖耗時 20ms,sleep 100ms,實際幀間隔 120ms → 實際幀率約 8.3 FPS,遠低於目標 10 FPS。隨著繪圖複雜度波動,幀率極不穩定。
記錄「下一幀理論開始時間」,每幀動態計算剩餘睡眠時間:
public void run() {
long tm = System.currentTimeMillis(); // 基準時間
while (Thread.currentThread() == animator) {
repaint();
try {
tm += delay; // 下一幀理論時間點
long sleepTime = tm - System.currentTimeMillis();
if (sleepTime > 0) Thread.sleep(sleepTime); // 僅補足差值
// 若 sleepTime <= 0,說明繪圖超時,立即進入下一幀(不睡眠)
} catch (InterruptedException e) {
break;
}
frame++;
}
}
優勢:
paint() / update() 中。start() 啟動,stop() 通過置空引用優雅停止,避免 Thread.stop() 等棄用方法。這套「線程驅動 + 時間基準校正」模式,不僅適用於 Applet,也是 Swing/JavaFX 遊戲循環、實時渲染引擎的基石思想。