在 C++Builder 中利用 TImage 元件與 TCanvas 繪圖介面,無需複雜的 DirectX/OpenGL,即可輕鬆實現視覺效果優異的圖片進場動畫。本文詳細講解兩種經典特效——中心擴散與百葉窗簾的演算法邏輯、核心代碼實作與優化技巧。
Image1),設定 Picture 載入 BMP/JPG/PNG 圖片。Image1->AutoSize = true 或手動調整 Width/Height 與圖片原始尺寸一致。btnCenterSpread (Caption="中心擴散")、btnBlinds (Caption="百葉窗簾")。原理:從圖片幾何中心點開始,逐漸增加顯示矩形的寬高,直到覆蓋完整圖片區域。每一幀計算當前顯示矩形的 Left, Top, Width, Height,並呼叫 Refresh() 觸發重繪。
數學模型 (假設原始圖片寬 W、高 H、中心點 (Cx, Cy)):
i 幀 (0 ≤ i ≤ W):顯示寬度 w = i,高度 h = i * H / W (保持長寬比)。Left = Cx - w/2, Top = Cy - h/2。void __fastcall TForm1::btnCenterSpreadClick(TObject *Sender) {
// 1. 記錄原始位置與尺寸
int origLeft = Image1->Left;
int origTop = Image1->Top;
int origW = Image1->Width;
int origH = Image1->Height;
// 2. 動畫幀迴圈 (以寬度為基準遞增)
for (int i = 0; i <= origW; i += 2) { // 步進 2 像素加速,可依效能調整
// 計算當前顯示矩形
int curW = i;
int curH = (curW * origH) / origW; // 整數運算保持比例
// 計算左上角使其居中
int curLeft = origLeft + (origW - curW) / 2;
int curTop = origTop + (origH - curH) / 2;
// 套用至 Image 元件 (觸發視覺更新)
Image1->Left = curLeft;
Image1->Top = curTop;
Image1->Width = curW;
Image1->Height = curH;
Image1->Refresh(); // 強制立即重繪
Application->ProcessMessages(); // 處理訊息佇列,保持 UI 回應
// 簡易幀率控制 (約 60 FPS = 16ms)
Sleep(10); // 可改用高精度計時器
}
// 3. 確保最終狀態完全正確
Image1->Left = origLeft;
Image1->Top = origTop;
Image1->Width = origW;
Image1->Height = origH;
Image1->Refresh();
}
原理:將圖片水平切割為 N 條等寬「葉片」。動畫過程中,逐行掃描:每一幀將每一片葉片的下一行像素從來源畫布複製到目標畫布。視覺上呈現「百葉窗由上而下逐漸打開」的效果。
關鍵技術:TCanvas::CopyRect() — 高效像素區塊複製 (GDI BitBlt 封裝)。
void __fastcall TForm1::btnBlindsClick(TObject *Sender) {
// 1. 參數設定
const int LEAF_COUNT = 16; // 葉片數量 (可調整,越多越細膩)
int imgW = Image1->Width;
int imgH = Image1->Height;
int leafH = imgH / LEAF_COUNT; // 每葉片高度 (像素)
// 2. 建立離屏位圖作為目標畫布 (避免螢幕閃爍)
Graphics::TBitmap *pBmp = new Graphics::TBitmap();
try {
pBmp->Width = imgW;
pBmp->Height = imgH;
pBmp->PixelFormat = pf24bit; // 或 pf32bit 提升複製效能
// 3. 動畫主迴圈:逐行掃描 (總行數 = leafH)
for (int scanLine = 0; scanLine < leafH; ++scanLine) {
// 遍歷每一片葉片
for (int leaf = 0; leaf < LEAF_COUNT; ++leaf) {
int srcY = leaf * leafH + scanLine; // 來源 Y 座標
int dstY = srcY; // 目標 Y 座標 (同位置)
// 定義來源矩形 (來源圖片的單一掃描線)
TRect srcRect(0, srcY, imgW, srcY + 1);
// 定義目標矩形 (離屏位圖對應位置)
TRect dstRect(0, dstY, imgW, dstY + 1);
// 高效複製:來源 Image1.Canvas -> 目標 pBmp.Canvas
pBmp->Canvas->CopyRect(dstRect, Image1->Canvas, srcRect);
}
// 4. 將離屏位圖一次性繪製到表單/容器畫布 (雙緩衝)
// 假設 Image1 置於 Form1 上,直接繪製到 Form1 畫布
// 實務上建議繪製到另一個 TImage 或 TPaintBox 的 Canvas
Form1->Canvas->Draw(Image1->Left, Image1->Top, pBmp);
Application->ProcessMessages();
Sleep(5); // 控制動畫速度
}
}
__finally {
delete pBmp; // 釋放資源
}
// 動畫結束後,確保原始 Image1 可見 (若動畫期間將其 Visible=false)
// Image1->Visible = true;
}
| 優化方向 | 做法 | 效果 |
|---|---|---|
| 消除閃爍 | 1. 表單 DoubleBuffered = true (BCB6+)。2. 使用離屏 TBitmap 雙緩衝 (如百葉窗範例)。3. 覆寫 WM_ERASEBKGND 返回 1 禁止背景擦除。 | 動畫流暢無撕裂 |
| 幀率獨立 | 使用 TTimer (Interval=16) 或多媒體計時器 (timeSetEvent) 驅動動畫狀態機,而非 Sleep 阻塞主執行緒。 | UI 保持回應,動畫速度穩定 |
| 加速複製 | TBitmap->PixelFormat = pf32bit (32位對齊) 配合 CopyRect 最快。大圖可考慮 StretchBlt / AlphaBlend (GDI+)。 | CPU 佔用降低 |
| 動畫曲線 | 引入 緩動函數 (Easing: EaseOutQuad, EaseInOutCubic) 計算每幀進度,取代線性遞增。 | 視覺更自然專業 |
| 方向變化 | 修改掃描順序:垂直百葉 (水平切割)、對角線、圓形擴散、馬賽克、淡入淡出 (AlphaBlend)。 | 豐富視覺效果庫 |
// 表單私有欄位
enum TAnimType { atNone, atCenterSpread, atBlinds };
TAnimType FAnimType = atNone;
int FAnimStep = 0, FAnimMaxStep = 0;
TBitmap *FOffscreenBmp = nullptr;
TTimer *FAnimTimer;
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
FAnimTimer = new TTimer(this);
FAnimTimer->Interval = 16; // ~60 FPS
FAnimTimer->OnTimer = AnimTimerTick;
FAnimTimer->Enabled = false;
}
void __fastcall TForm1::StartAnimation(TAnimType type) {
if (FAnimType != atNone) return; // 防止重入
FAnimType = type;
FAnimStep = 0;
if (type == atCenterSpread) {
FAnimMaxStep = Image1->Width / 2; // 步進 2px
} else if (type == atBlinds) {
const int LEAF_COUNT = 16;
FAnimMaxStep = Image1->Height / LEAF_COUNT; // 掃描行數
// 建立離屏位圖
FOffscreenBmp = new TBitmap();
FOffscreenBmp->Width = Image1->Width;
FOffscreenBmp->Height = Image1->Height;
FOffscreenBmp->PixelFormat = pf32bit;
}
FAnimTimer->Enabled = true;
}
void __fastcall TForm1::AnimTimerTick(TObject *Sender) {
if (FAnimType == atCenterSpread) {
// ... 執行中心擴散單步 (同前代碼,但無 for 迴圈,僅單步 FAnimStep++)
if (FAnimStep >= FAnimMaxStep) StopAnimation();
} else if (FAnimType == atBlinds) {
// ... 執行百葉窗單步掃描 (同前代碼,單步 FAnimStep++)
if (FAnimStep >= FAnimMaxStep) StopAnimation();
}
FAnimStep++;
}
void __fastcall TForm1::StopAnimation() {
FAnimTimer->Enabled = false;
if (FOffscreenBmp) { delete FOffscreenBmp; FOffscreenBmp = nullptr; }
FAnimType = atNone;
// 還原 Image1 顯示
Image1->Refresh();
}
// 按鈕事件
void __fastcall TForm1::btnCenterSpreadClick(TObject *Sender) { StartAnimation(atCenterSpread); }
void __fastcall TForm1::btnBlindsClick(TObject *Sender) { StartAnimation(atBlinds); }
善用 TImage 幾何屬性控制 (Left/Top/Width/Height) 實現幾何變換動畫,配合 TCanvas::CopyRect 像素級操作實現紋理掃描動畫,是 C++Builder VCL 圖形程式設計的兩大核心技法。掌握離屏雙緩衝、計時器驅動狀態機、緩動函數等進階模式,即可構建專業級的 UI 動畫效果,大幅提升應用程式的使用者體驗。