架構設計
- 衍生
CWnd 建立無標題彈出視窗 (WS_POPUP)。
- 載入位圖資源,建立記憶體 DC 緩存。
WM_PAINT 中執行隨機積木塊動畫:將位圖分割為 N×M 格,隨機順序逐塊 StretchBlt 到螢幕 DC。
- 動畫完成後可自動關閉或等待主程式初始化完成通知關閉。
類別宣告
class CSplashWnd : public CWnd {
CDC m_memDC; // 記憶體 DC 持有位圖
CBitmap m_bmpSplash; // 位圖物件
BITMAP m_bm; // 位圖尺寸資訊
CBitmap* m_pOldBmp; // 記憶體 DC 原位圖
BOOL m_bAnimating; // 動畫進行中旗標
public:
CSplashWnd();
BOOL Create(UINT nBmpResID); // 建立視窗並載入位圖
void StartAnimation(); // 觸發動畫 (通常在 OnPaint 首次呼叫)
protected:
afx_msg void OnPaint();
afx_msg void OnTimer(UINT_PTR nIDEvent);
DECLARE_MESSAGE_MAP()
};
建立與初始化
CSplashWnd::CSplashWnd() : m_pOldBmp(NULL), m_bAnimating(FALSE) {}
BOOL CSplashWnd::Create(UINT nBmpResID) {
m_bmpSplash.LoadBitmap(nBmpResID);
m_bmpSplash.GetBitmap(&m_bm);
// 註冊視窗類別
CString strClass = AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW));
return CreateEx(0, strClass, _T("Splash"), WS_POPUP | WS_VISIBLE,
0, 0, m_bm.bmWidth, m_bm.bmHeight, NULL, NULL, NULL);
}
void CSplashWnd::OnPaint() {
CPaintDC dc(this); // 螢幕 DC
if (!m_bAnimating) {
// 首次繪製:建立記憶體 DC,啟動動畫
m_memDC.CreateCompatibleDC(&dc);
m_pOldBmp = m_memDC.SelectObject(&m_bmpSplash);
m_bAnimating = TRUE;
SetTimer(1, 10, NULL); // 10ms 間隔驅動動畫
}
// 動畫由 OnTimer 驅動,這裡僅處理動畫完成後的最終全圖顯示
if (!m_bAnimating) { // 動畫結束旗標在 OnTimer 中設定
dc.BitBlt(0, 0, m_bm.bmWidth, m_bm.bmHeight, &m_memDC, 0, 0, SRCCOPY);
}
}
隨機積木塊動畫核心 (OnTimer)
void CSplashWnd::OnTimer(UINT_PTR nIDEvent) {
if (nIDEvent != 1) return;
static int s_grid[20][20] = {0}; // 20x20 格,0=未顯示, 1=已顯示
static int s_shown = 0;
const int TOTAL = 20 * 20;
if (s_shown >= TOTAL) {
KillTimer(1);
m_bAnimating = FALSE;
Invalidate(); // 觸發 OnPaint 顯示完整圖
return;
}
// 尋找下一個未顯示格子 (隨機)
int x, y;
do {
x = rand() % 20;
y = rand() % 20;
} while (s_grid[x][y]);
s_grid[x][y] = 1;
s_shown++;
// 計算來源/目標矩形
int sw = m_bm.bmWidth / 20;
int sh = m_bm.bmHeight / 20;
int sx = x * sw;
int sy = y * sh;
CClientDC dc(this);
dc.StretchBlt(sx, sy, sw, sh, &m_memDC, sx, sy, sw, sh, SRCCOPY);
}
清理與關閉
CSplashWnd::~CSplashWnd() {
if (m_memDC.m_hDC) {
m_memDC.SelectObject(m_pOldBmp);
m_memDC.DeleteDC();
}
m_bmpSplash.DeleteObject();
}
void CSplashWnd::CloseSplash() {
KillTimer(1);
DestroyWindow();
}
主程式整合 (CWinApp::InitInstance)
BOOL CMyApp::InitInstance() {
// ... 標準初始化 ...
CSplashWnd* pSplash = new CSplashWnd;
pSplash->Create(IDB_SPLASH);
pSplash->CenterWindow();
pSplash->ShowWindow(SW_SHOW);
pSplash->UpdateWindow();
// 執行耗時初始化 (資料庫連線、載入設定、檢查更新...)
// 期間可呼叫 pSplash->SetMessage(_T("正在載入模組...")) 更新文字 (需擴充)
// 初始化完成
pSplash->CloseSplash();
delete pSplash;
// ... 建立主框架視窗 ...
return TRUE;
}
進階特效擴充
- 淡入/淡出:
AlphaBlend 漸變 Alpha 值 (需 32-bit 位圖或建立 Alpha 通道)。
- 水波/百葉窗/捲軸: 修改
OnTimer 矩形計算邏輯 (按行/列/對角線順序)。
- 進度文字: 記憶體 DC 上
TextOut 繪製進度文字,再 BitBlt 該區域。
- 多執行緒: 主執行緒顯示 Splash,工作執行緒初始化,完成
PostMessage 通知關閉。
常見問題
| 問題 | 解決 |
| 動畫閃爍 | 雙緩衝:所有繪圖在記憶體 DC,僅 StretchBlt 單塊到螢幕 |
| 位圖資源 ID 找不到 | #include "resource.h" 在類別頭檔,確認資源編譯進 .res |
| 視窗未置中 | CenterWindow() 需在 Create 後呼叫 |
| 主程式初始化卡死,Splash 無法關閉 | 耗時操作移至工作執行緒,或定期 PeekMessage 處理 Splash 訊息 |