所有動畫的核心原理皆是利用人眼的視覺暫留特性:在短時間內快速切換靜態畫面,形成連續動態的錯覺。J2ME 中透過 Canvas 重繪機制配合執行緒排程,即可實現從簡單的文字閃爍到複雜的角色幀動畫。
以下範例展示每 0.2 秒切換一次顯示狀態,實現文字閃爍效果:
package welcomecanvas;
import javax.microedition.lcdui.*;
public class DongHuaCanvas extends Canvas implements Runnable {
private boolean visible = true; // 控制文字顯示/隱藏
public DongHuaCanvas() {
new Thread(this).start(); // 啟動動畫執行緒
}
protected void paint(Graphics g) {
// 清除畫布(白底)
g.setColor(0xFFFFFF);
g.fillRect(0, 0, getWidth(), getHeight());
// 繪製文字(黑色)
g.setColor(0x000000);
if (visible) {
g.drawString("閃爍的文字", 50, 50, Graphics.LEFT | Graphics.TOP);
}
}
public void run() {
while (true) {
try {
Thread.sleep(200); // 幀間隔 200ms (5 FPS)
} catch (InterruptedException ignored) {}
visible = !visible; // 切換狀態
repaint(); // 觸發重繪
}
}
}
| 關鍵點 | 說明 |
|---|---|
| 雙重緩衝 | MIDP 2.0+ 自動支援雙重緩衝,repaint() 會排程重繪而非立即執行,避免畫面撕裂。 |
| 執行緒安全 | 動畫邏輯必須在獨立執行緒運行,repaint() 是少數可跨執行緒安全呼叫的 UI 方法。 |
| 幀率控制 | Thread.sleep() 控制幀間隔,實際幀率受裝置效能影響,建議加入時間差修正。 |
複雜動畫(如角色走路、爆炸特效)本質上是依序播放一組預載入的圖片幀。擴充架構如下:
public class SpriteAnimation extends Canvas implements Runnable {
private Image[] frames; // 動畫幀陣列
private int currentFrame = 0;
private long frameDelay = 100; // 毫秒
private long lastUpdate = 0;
public void run() {
while (true) {
long now = System.currentTimeMillis();
if (now - lastUpdate >= frameDelay) {
currentFrame = (currentFrame + 1) % frames.length;
lastUpdate = now;
repaint();
}
try { Thread.sleep(10); } catch (Exception ignored) {}
}
}
protected void paint(Graphics g) {
if (frames != null && frames[currentFrame] != null) {
g.drawImage(frames[currentFrame], getWidth()/2, getHeight()/2,
Graphics.HCENTER | Graphics.VCENTER);
}
}
}
startApp() 階段一次載入所有幀,避免運行時 GC 卡頓。drawRegion() 或 setClip() 繪製,減少 Image 物件開銷。frameDelay,低端機種可降至 10-15 FPS。