在遊戲選單、三維軟體工具列中常見「滑鼠移過按鈕播放微動畫」的效果。MFC CAnimateCtrl 封裝了 Windows 動畫控制項,支援無聲、未壓縮或 RLE 壓縮的 AVI 檔案/資源。本文示範如何將 CAnimateCtrl 嵌入 CButton 衍生類,在 WM_MOUSEMOVE 觸發播放,打造專業級動態按鈕。
DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | ACS_AUTOPLAY | ACS_CENTER | ACS_TRANSPARENT;
| 樣式 | 說明 |
|---|---|
ACS_CENTER | 動畫置中,控制項尺寸固定不隨動畫大小變化 |
ACS_TRANSPARENT | 背景透明(不繪製動畫背景色) |
ACS_AUTOPLAY | 開啟後自動循環播放 |
| 函數 | 用途 |
|---|---|
Open(LPCTSTR lpszFileName) / Open(UINT nID) | 從檔案/資源開啟 AVI |
Play(UINT nFrom, UINT nTo, UINT nRep) | 播放:起始幀/結束幀/重複次數 (-1=無限) |
Seek(UINT nTo) | 靜態顯示指定幀 |
Stop() | 停止播放 |
Close() | 關閉並釋放記憶體 |
class CAviButton : public CButton
{
public:
CAviButton();
UINT m_nAviID; // AVI 資源 ID
CAnimateCtrl m_animCtrl; // 內嵌動畫控制項
BOOL m_bPlaying; // 播放狀態旗標
void LoadAvi(UINT nID);
void DrawButton(CDC* pDC, UINT nState, CRect rect); // 繪製按鈕邊框/背景
virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS); // OwnerDraw 入口
virtual BOOL Create(LPCTSTR, LPCTSTR, DWORD, const RECT&, CWnd*, UINT, CCreateContext* = NULL);
protected:
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CAviButton, CButton)
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
void CAviButton::OnMouseMove(UINT nFlags, CPoint point)
{
ClientToScreen(&point);
CRect rcWnd; GetWindowRect(&rcWnd);
BOOL bOver = rcWnd.PtInRect(point);
if (bOver && IsWindowEnabled()) {
if (::IsWindow(m_animCtrl.m_hWnd) && !m_bPlaying) {
m_animCtrl.Play(0, -1, 1); // 播放一次
m_bPlaying = TRUE;
SetCapture(); // 捕捉滑鼠以便偵測移出
}
} else {
m_bPlaying = FALSE;
ReleaseCapture();
}
CButton::OnMouseMove(nFlags, point);
}
void CAviButton::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
CRect rcClient; GetClientRect(&rcClient);
// 首次建立動畫控制項(填滿按鈕客戶區)
if (!::IsWindow(m_animCtrl.m_hWnd)) {
m_animCtrl.Create(WS_CHILD | WS_VISIBLE, rcClient, this, 0);
m_animCtrl.Open(m_nAviID); // 開啟並顯示第一幀
}
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
UINT nState = lpDIS->itemState;
DrawButton(pDC, nState, rcClient); // 自繪 3D 邊框/背景
}
void CAviButton::DrawButton(CDC* pDC, UINT nState, CRect rect)
{
COLORREF clrTopLeft, clrBottomRight, clrEdge;
if (nState & ODS_SELECTED) { // 按下態
clrTopLeft = RGB(0,0,0); clrBottomRight = RGB(128,128,128); clrEdge = RGB(128,128,128);
} else { // 正常態
clrTopLeft = RGB(255,255,255); clrBottomRight = RGB(128,128,128); clrEdge = RGB(0,0,0);
}
CPen penTL(PS_SOLID, 1, clrTopLeft), penBR(PS_SOLID, 1, clrBottomRight), penEdge(PS_SOLID, 1, clrEdge);
CPen* pOld = pDC->SelectObject(&penTL);
// 左上邊框
pDC->MoveTo(1, rect.Height()-1); pDC->LineTo(1,1); pDC->LineTo(rect.Width()-1,1);
pDC->MoveTo(0, rect.Height()-1); pDC->LineTo(0,0); pDC->LineTo(rect.Width()-1,0);
pDC->SelectObject(&penBR);
// 右下邊框
pDC->MoveTo(rect.Width()-1,0); pDC->LineTo(rect.Width()-1,rect.Height()-1); pDC->LineTo(0,rect.Height()-1);
pDC->MoveTo(rect.Width()-2,1); pDC->LineTo(rect.Width()-2,rect.Height()-2); pDC->LineTo(0,rect.Height()-2);
pDC->SelectObject(&penEdge); // 外框
pDC->SelectObject(pOld);
}
AVI → ID 為 IDR_AVI。Owner Draw = True → 類別向導建立 CAviButton 變數 m_aviBtn。OnInitDialog 中載入資源:m_aviBtn.LoadAvi(IDR_AVI);
滑鼠移入按鈕區域 → 播放 AVI 動畫一次;移出 → 停止並釋放捕捉。
CAnimateCtrl 嵌入 CToolBar 自定按鈕(TBSTYLE_CUSTOMERASE)。ACS_AUTOPLAY + Play(0,-1,-1) 實現持續播放的載入指示器。CAnimateCtrl + OwnerDraw Button 是實現輕量級 AVI 動畫按鈕的經典組合。關鍵在於 DrawItem 中延遲建立動畫控制項(確保視窗句柄有效),以及 WM_MOUSEMOVE + SetCapture/ReleaseCapture 精確控制播放生命週期。受限於 AVI 格式限制,現代專案建議改用 CMFCToolBarButton + GIF/APNG 解碼庫實現更豐富的動態介面。