首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 動畫編程基礎(二):幀渲染、圖形生成與雙緩衝消除閃爍

     編輯:Java 相關知識

核心任務:繪製每一幀

動畫本質是快速連續繪製一系列靜態圖像(幀)。前文已構建動畫循環線程,此處聚焦 paint(Graphics g) 方法中的繪圖邏輯。

基礎繪製示例

public void paint(Graphics g) {
    g.setColor(Color.black);
    g.drawString("Frame " + frame, 0, 30);
}

每幀調用 repaint() 觸發 AWT 回調 paint(),在屏幕指定位置繪製幀號。

進階:繪製動態正弦波組合圖形

對每個 x 座標畫一條垂直短線,隨幀號 frame 變化產生波動效果:

public void paint(Graphics g) {
    Dimension d = size();
    int h = d.height / 2;
    for (int x = 0; x < d.width; x++) {
        int y1 = (int)((1.0 + Math.sin((x - frame) * 0.05)) * h);
        int y2 = (int)((1.0 + Math.sin((x + frame) * 0.05)) * h);
        g.drawLine(x, y1, x, y2); // 注意:原文 g.DrawLine 大寫錯誤,Java 區分大小寫
    }
}

問題: 直接在 paint() 中大量計算並繪圖,會導致明顯閃爍。

閃爍成因分析

  1. 繪製耗時過長:每幀計算量大,導致幀間隔拉長。
  2. 背景清除與繪圖非原子:AWT 默認在調用 paint() 前自動清除背景(調用 update()),清除到繪製完成的間隙,用戶看到空白背景 → 閃爍。

PC 平台閃爍明顯,X Window 因圖形緩存較輕微。

兩大消除閃爍方案

方案一:重寫 update() 方法(增量繪圖)

AWT 重繪流程:repaint() → update() → paint()。默認 update() 清屏再調 paint()。
重寫 update(),將繪圖邏輯移入,並僅擦除上一幀痕跡,不再全屏清屏:

public void paint(Graphics g) {
    update(g); // paint 直接委託 update
}

public void update(Graphics g) {
    Color bg = getBackground();
    Dimension d = size();
    int h = d.height / 2;
    for (int x = 0; x < d.width; x++) {
        int y1 = (int)((1.0 + Math.sin((x - frame) * 0.05)) * h);
        int y2 = (int)((1.0 + Math.sin((x + frame) * 0.05)) * h);
        if (y1 > y2) { int t = y1; y1 = y2; y2 = t; } // 確保 y1 <= y2

        // 1. 擦除上一幀該列像素(畫背景色線)
        g.setColor(bg);
        g.drawLine(x, 0, x, y1);
        g.drawLine(x, y2, x, d.height);

        // 2. 繪製當前幀該列像素
        g.setColor(Color.black);
        g.drawLine(x, y1, x, y2);
    }
}

原理: 背景不再自動清除,手動僅擦除會變化的垂直線區域,實現「增量更新」,徹底消除閃爍。

方案二:雙緩衝(Double Buffering)—— 現代標準做法

雖本文未展開,但業界標準解法是:

  1. 創建一個屏幕外 Image(或 BufferedImage)作為後台緩衝區。
  2. 所有繪圖操作在後台緩衝區完成。
  3. 繪製完畢,一次性將緩衝區內容 drawImage() 到屏幕 Graphics。

Java Swing 組件默認開啟雙緩衝(JComponent.setDoubleBuffered(true)),AWT 需手動實現。

小結

  • 動畫核心是高頻調用 repaint() 觸發 paint()。
  • 閃爍源於「背景清除」與「業務繪圖」分離導致的視覺間隙。
  • 重寫 update() 實現增量繪圖是 AWT 時代經典技巧;現代開發請直接使用 Swing 雙緩衝機制。
  • 代碼細節:Math.sin 非 math.sin,g.drawLine 非 g.DrawLine,Java 嚴格區分大小寫。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved