首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 製作播放 AVI 視訊流的動畫按鈕

     編輯:VC 教學

動畫按鈕概念

AVI 動畫按鈕:滑鼠移過按鈕時自動播放 AVI 視訊流,滑鼠移出停止。廣泛應用於遊戲介面、3D 軟體(如摩托英豪、Cool 3D),增強介面動感與專業度。

核心技術:MFC CAnimateCtrl 類別

CAnimateCtrl 封裝 Windows 動畫控件,支援播放無聲、未壓縮或 RLE 壓縮的 AVI 檔案/資源。

建立動畫控件


BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);

常用樣式組合

樣式說明
WS_CHILD | WS_VISIBLE | WS_BORDER基本視窗樣式(必須)
ACS_CENTER動畫置中,控件尺寸固定
ACS_TRANSPARENT背景透明
ACS_AUTOPLAY開啟後自動循環播放

典型建立:WS_CHILD|WS_VISIBLE|WS_BORDER|ACS_AUTOPLAY|ACS_CENTER|ACS_TRANSPARENT

核心成員函式

函式功能參數說明
Open(LPCTSTR lpszFileName)從檔案開啟 AVI檔案路徑,NULL=關閉
Open(UINT nID)從資源開啟 AVI資源 ID
Play(UINT nFrom, UINT nTo, UINT nRep)播放起始幀、結束幀(-1=結尾)、重複次數(-1=無限)
Seek(UINT nTo)靜態顯示指定幀幀索引 (0=第一幀, -1=最後一幀)
Stop()停止播放-
Close()關閉並釋放記憶體-

AVI 檔案限制(重要)

動畫控件只能播放符合以下條件的 AVI:

  • 無聲(無音訊軌道)
  • 未壓縮 或 RLE 壓縮
  • 調色盤固定不變
  • 最大 256 色,但實際顯示受限於系統預設調色盤(類 16 色效果)

若需播放高品質 AVI,請使用 MCI 介面或 DirectShow。

完整實作:CAviButton 類別

1. 類別宣告


// AviButton.h
#pragma once

class CAviButton : public CButton
{
    DECLARE_DYNAMIC(CAviButton)

public:
    CAviButton();
    virtual ~CAviButton();

protected:
    CAnimateCtrl m_animCtrl;   // 動畫控件
    BOOL m_bMouseOver;         // 滑鼠是否在按鈕上
    UINT m_nAviResourceID;     // AVI 資源 ID
    CRect m_animRect;          // 動畫顯示區域

    // 重寫
    virtual void PreSubclassWindow();
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    
    // 訊息處理
    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    afx_msg void OnMouseLeave();
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnDestroy();
    
    DECLARE_MESSAGE_MAP()
    
public:
    void SetAviResource(UINT nResourceID);
    void SetAviFile(LPCTSTR lpszFileName);
};

2. 核心實作


// AviButton.cpp
#include "stdafx.h"
#include "AviButton.h"

IMPLEMENT_DYNAMIC(CAviButton, CButton)

BEGIN_MESSAGE_MAP(CAviButton, CButton)
    ON_WM_MOUSEMOVE()
    ON_WM_MOUSELEAVE()
    ON_WM_CREATE()
    ON_WM_DESTROY()
END_MESSAGE_MAP()

CAviButton::CAviButton() : m_bMouseOver(FALSE), m_nAviResourceID(0) {}

CAviButton::~CAviButton() {}

int CAviButton::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CButton::OnCreate(lpCreateStruct) == -1) return -1;
    
    // 設定 Owner-Draw
    ModifyStyle(0, BS_OWNERDRAW);
    
    // 建立動畫控件(隱藏,僅作為播放引擎)
    CRect rect(0, 0, 0, 0);
    DWORD dwStyle = WS_CHILD | ACS_AUTOPLAY | ACS_CENTER | ACS_TRANSPARENT;
    if (!m_animCtrl.Create(dwStyle, rect, this, 100)) {
        TRACE0("動畫控件建立失敗\n");
        return -1;
    }
    m_animCtrl.ShowWindow(SW_HIDE);  // 隱藏控件本身
    
    return 0;
}

void CAviButton::OnDestroy()
{
    m_animCtrl.Close();
    CButton::OnDestroy();
}

void CAviButton::PreSubclassWindow()
{
    ModifyStyle(0, BS_OWNERDRAW);
    CButton::PreSubclassWindow();
}

void CAviButton::SetAviResource(UINT nResourceID)
{
    m_nAviResourceID = nResourceID;
    if (m_animCtrl.GetSafeHwnd()) {
        m_animCtrl.Open(nResourceID);
    }
}

void CAviButton::SetAviFile(LPCTSTR lpszFileName)
{
    if (m_animCtrl.GetSafeHwnd()) {
        m_animCtrl.Open(lpszFileName);
    }
}

void CAviButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
    CRect rect = lpDrawItemStruct->rcItem;
    UINT nState = lpDrawItemStruct->itemState;
    
    int nSavedDC = pDC->SaveDC();
    
    // 1. 繪製按鈕背景
    DrawButtonBackground(pDC, rect, nState);
    
    // 2. 繪製按鈕文字
    DrawButtonText(pDC, rect, nState);
    
    // 3. 若滑鼠在上方且有 AVI,在按鈕內顯示動畫
    if (m_bMouseOver && m_animCtrl.GetSafeHwnd()) {
        DrawAnimationInButton(pDC, rect);
    }
    
    pDC->RestoreDC(nSavedDC);
}

void CAviButton::DrawButtonBackground(CDC* pDC, CRect rect, UINT nState)
{
    BOOL bPressed = (nState & ODS_SELECTED);
    BOOL bFocused = (nState & ODS_FOCUS);
    BOOL bDisabled = (nState & ODS_DISABLED);
    
    // 使用系統主題繪製(XP+)
    #if (_WIN32_WINNT >= 0x0501)
    if (IsAppThemed()) {
        HTHEME hTheme = OpenThemeData(m_hWnd, L"BUTTON");
        if (hTheme) {
            int nStateId = bDisabled ? PBS_DISABLED : (bPressed ? PBS_PRESSED : (bFocused ? PBS_HOT : PBS_NORMAL));
            DrawThemeBackground(hTheme, pDC->GetSafeHdc(), BP_PUSHBUTTON, nStateId, &rect, NULL);
            CloseThemeData(hTheme);
            return;
        }
    }
    #endif
    
    // 回退:傳統 3D 繪製
    COLORREF clrFace = ::GetSysColor(COLOR_3DFACE);
    CBrush brush(clrFace);
    pDC->FillRect(rect, &brush);
    
    if (bPressed) {
        pDC->Draw3dRect(rect, ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DHIGHLIGHT));
    } else {
        pDC->Draw3dRect(rect, ::GetSysColor(COLOR_3DHIGHLIGHT), ::GetSysColor(COLOR_3DSHADOW));
    }
}

void CAviButton::DrawButtonText(CDC* pDC, CRect rect, UINT nState)
{
    CString strText;
    GetWindowText(strText);
    if (strText.IsEmpty()) return;
    
    COLORREF clrText = (nState & ODS_DISABLED) ? ::GetSysColor(COLOR_GRAYTEXT) : ::GetSysColor(COLOR_BTNTEXT);
    pDC->SetTextColor(clrText);
    pDC->SetBkMode(TRANSPARENT);
    
    // 文字區域避開動畫區(假設動畫在右側)
    CRect textRect = rect;
    textRect.DeflateRect(8, 4);
    if (m_bMouseOver) textRect.right -= 40;  // 為動畫留空間
    
    pDC->DrawText(strText, textRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}

void CAviButton::DrawAnimationInButton(CDC* pDC, CRect rect)
{
    // 在按鈕右側顯示動畫(40x40 區域)
    CRect animRect(rect.right - 42, rect.top + (rect.Height()-40)/2, rect.right - 2, rect.top + (rect.Height()+40)/2);
    
    // 將動畫控件移至該位置並顯示
    m_animCtrl.SetWindowPos(NULL, animRect.left, animRect.top, animRect.Width(), animRect.Height(),
                           SWP_NOZORDER | SWP_SHOWWINDOW);
    
    // 強制動畫控件重繪到按鈕 DC(進階:實際需子類別化動畫控件的 WM_PAINT)
    // 簡易做法:動畫控件本身有視窗,會自動疊加顯示
}

void CAviButton::OnMouseMove(UINT nFlags, CPoint point)
{
    if (!m_bMouseOver) {
        m_bMouseOver = TRUE;
        
        // 開始追蹤 MouseLeave
        TRACKMOUSEEVENT tme;
        tme.cbSize = sizeof(TRACKMOUSEEVENT);
        tme.dwFlags = TME_LEAVE;
        tme.hwndTrack = m_hWnd;
        _TrackMouseEvent(&tme);
        
        // 開始播放動畫
        if (m_animCtrl.GetSafeHwnd()) {
            m_animCtrl.Play(0, -1, -1);  // 從頭播放到尾,無限循環
        }
        
        Invalidate();  // 重繪顯示動畫
    }
    CButton::OnMouseMove(nFlags, point);
}

void CAviButton::OnMouseLeave()
{
    m_bMouseOver = FALSE;
    
    // 停止動畫
    if (m_animCtrl.GetSafeHwnd()) {
        m_animCtrl.Stop();
        m_animCtrl.Seek(0);  // 回到第一幀
        m_animCtrl.ShowWindow(SW_HIDE);
    }
    
    Invalidate();  // 重繪隱藏動畫
    CButton::OnMouseLeave();
}

專案建立步驟

  1. VC6.0 → New Project → MFC AppWizard (exe) → Dialog based → 專案名 TestAviButton
  2. ClassWizard (Ctrl+W) → Add Class → 基類 CButton → 新類別 CAviButton
  3. 對話方塊編輯器:刪除 Cancel 按鈕,修改 OK 按鈕:
     Caption 空白、Style = Owner Draw、ID = IDC_MYAVIBTN
  4. 加入 AVI 資源:Insert → Resource → Import → 選擇 .avi 檔 → 資源類型輸入 AVI → ID 為 IDR_AVI
  5. 對話方塊類別中加入控件變數:CAviButton m_btnAvi;,DoDataExchange 中 DDX_Control(pDX, IDC_MYAVIBTN, m_btnAvi);
  6. OnInitDialog 中初始化:m_btnAvi.SetAviResource(IDR_AVI);
  7. 編譯執行,滑鼠移過按鈕即播放 AVI

進階技巧

1. 透過子類別化實現真正的 DC 繪製

上述簡易做法讓動畫控件疊加顯示。專業做法是子類別化 CAnimateCtrl,攔截 WM_PAINT,將幀繪製到按鈕的 DC 上:


class CEmbeddedAnimateCtrl : public CAnimateCtrl {
    CAviButton* m_pParentBtn;
public:
    void SetParentBtn(CAviButton* p) { m_pParentBtn = p; }
protected:
    afx_msg void OnPaint() {
        if (m_pParentBtn) {
            // 取得父按鈕 DC,在指定區域繪製當前幀
            CDC* pDC = m_pParentBtn->GetDC();
            CRect rect = m_pParentBtn->GetAnimationRect();
            // 這裡需用 GetDIBits 等取得當前幀位圖並繪製
            ReleaseDC(pDC);
        }
    }
    DECLARE_MESSAGE_MAP()
};

2. 支援多種觸發模式


enum TriggerMode { HOVER, CLICK, FOCUS };
TriggerMode m_triggerMode = HOVER;

// OnLButtonDown 中
if (m_triggerMode == CLICK) { PlayAnimation(); }

3. 動畫預載入與快取


void CAviButton::PreloadAnimation()
{
    // 在背景執行緒預載入
    if (m_nAviResourceID) {
        m_animCtrl.Open(m_nAviResourceID);
        m_animCtrl.Seek(0);
        m_animCtrl.Close();  // 關閉但快取於記憶體(視實作而定)
    }
}

常見問題

問題原因解決
AVI 不播放檔案格式不支援(有聲音、非 RLE 壓縮、調色盤變化)用視訊轉換工具輸出:無聲、未壓縮/RLE、固定調色盤、256 色以下
顏色失真動畫控件使用系統預設調色盤設計 AVI 時使用系統 20 色調色盤、或改用 MCI/DirectShow
動畫不顯示控件未建立、資源 ID 錯誤、OwnerDraw 未設定檢查 Create 回傳值、確認資源類型為 AVI、BS_OWNERDRAW
閃爍重繪頻率過高雙緩衝繪製、減少 Invalidate 範圍

結語

利用 CAnimateCtrl 與 CButton Owner-Draw 機制,可輕鬆打造具備 AVI 動畫效果的按鈕。關鍵在於:理解 AVI 格式限制、正確處理滑鼠進入/離開事件控制播放狀態、妥善整合動畫控件與按鈕繪製邏輯。此技術可擴展至工具列按鈕、狀態指示器等多種介面元素。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved