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

VC++ 製作透明不規則視窗桌面精靈:位圖透明化 + 定時動畫 + WAV 資源播放

     編輯:VC++

前言

想在桌面放置一個會唱歌、會動畫、造型不規則的小精靈嗎?關鍵技術只有三項:視窗區域裁切 實現透明不規則外型、定時器切換位圖 產生動畫、PlaySound 播放內嵌 WAV 資源。本文提供完整 CTransparentWnd 類別,可直接整合至任何 MFC 專案。

核心類別設計

class CTransparentWnd : public CWnd
{
public:
    CTransparentWnd();
    void CreateTransparent(LPCTSTR pszTitle, CRect& rect);
    void SetupRegion(CDC* pDC);      // 依遮色建立視窗區域
    void DoChange();                 // 切換動畫幀
    void SoundPlay();                // 播放 WAV 資源

protected:
    CBitmap m_bmpDraw;               // 目前顯示位圖
    int     m_nAniIndex = 0;         // 動畫幀索引

    afx_msg int OnCreate(LPCREATESTRUCT);
    afx_msg BOOL OnEraseBkgnd(CDC*);
    afx_msg void OnTimer(UINT_PTR);
    afx_msg void OnLButtonDown(UINT, CPoint);  // 拖曳移動
    afx_msg void OnRButtonDown(UINT, CPoint);  // 右鍵關閉
    DECLARE_MESSAGE_MAP()
};

關鍵實作細節

1. 建立無標題彈出視窗

void CTransparentWnd::CreateTransparent(LPCTSTR pszTitle, CRect& rect)
{
    CreateEx(0,
        AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
        pszTitle,
        WS_POPUP,                    // 無標題、無邊框
        rect,
        NULL, NULL, NULL);
    DoChange();                      // 載入第一幀並建立區域
}

2. 透明色區域裁切 (SetupRegion)

void CTransparentWnd::SetupRegion(CDC* pDC)
{
    CDC memDC; memDC.CreateCompatibleDC(pDC);
    CBitmap* pOld = memDC.SelectObject(&m_bmpDraw);

    BITMAP bm; m_bmpDraw.GetObject(sizeof(bm), &bm);
    COLORREF clrMask = memDC.GetPixel(0, 0);  // 左上角像素作為透明色

    CRgn wndRgn, tempRgn;
    wndRgn.CreateRectRgn(0, 0, bm.bmWidth, bm.bmHeight);

    for (int x = 0; x <= bm.bmWidth; x++)
        for (int y = 0; y <= bm.bmHeight; y++)
            if (memDC.GetPixel(x, y) == clrMask)
            {
                tempRgn.CreateRectRgn(x, y, x+1, y+1);
                wndRgn.CombineRgn(&wndRgn, &tempRgn, RGN_XOR);
                tempRgn.DeleteObject();
            }

    memDC.SelectObject(pOld);
    SetWindowRgn((HRGN)wndRgn.Detach(), TRUE);  // 系統接管區域物件
    MoveWindow(0, 0, bm.bmWidth, bm.bmHeight);  // 調整視窗大小符合位圖
}

原理: 掃描每個像素,將透明色像素從矩形區域中 RGN_XOR 剔除,剩餘區域即為可見非矩形視窗。

3. 動畫幀切換

void CTransparentWnd::DoChange()
{
    CString strRes; strRes.Format(_T("WEST%d"), m_nAniIndex % 2 + 1); // WEST1, WEST2
    m_bmpDraw.DeleteObject();
    m_bmpDraw.LoadBitmap(strRes);

    CWindowDC dc(this);
    SetupRegion(&dc);
    Invalidate();  // 觸發 OnEraseBkgnd 重繪
}

4. WAV 資源非同步播放

void CTransparentWnd::SoundPlay()
{
    // 停止上一輪播放
    PlaySound(_T("WEST"), AfxGetResourceHandle(),
              SND_RESOURCE | SND_PURGE | SND_NODEFAULT);

    // 設定播放長度計時器 (依實際 WAV 長度調整,單位 ms)
    SetTimer(2, 61080, NULL);

    // 非同步播放
    PlaySound(_T("WEST"), AfxGetResourceHandle(),
              SND_RESOURCE | SND_ASYNC | SND_NODEFAULT);
}

5. 訊息處理

BEGIN_MESSAGE_MAP(CTransparentWnd, CWnd)
    ON_WM_CREATE()
    ON_WM_ERASEBKGND()
    ON_WM_TIMER()
    ON_WM_LBUTTONDOWN()
    ON_WM_RBUTTONDOWN()
END_MESSAGE_MAP()

int CTransparentWnd::OnCreate(LPCREATESTRUCT lpCS)
{
    if (CWnd::OnCreate(lpCS) == -1) return -1;
    SetTimer(1, 1000, NULL);   // 動畫幀切換間隔
    SetTimer(2, 500, NULL);    // 啟動聲音播放
    SetWindowPos(&wndTopMost, 0,0,0,0, SWP_NOSIZE|SWP_NOMOVE);
    return 0;
}

BOOL CTransparentWnd::OnEraseBkgnd(CDC* pDC)
{
    CRect rc; GetWindowRect(&rc);
    CDC memDC; memDC.CreateCompatibleDC(pDC);
    CBitmap* pOld = memDC.SelectObject(&m_bmpDraw);
    pDC->BitBlt(0, 0, rc.Width(), rc.Height(), &memDC, 0, 0, SRCCOPY);
    memDC.SelectObject(pOld);
    return TRUE;  // 禁止預設背景擦除,減少閃爍
}

void CTransparentWnd::OnTimer(UINT_PTR nIDEvent)
{
    switch (nIDEvent)
    {
    case 1: DoChange(); break;   // 動畫
    case 2: SoundPlay(); break;  // 聲音循環
    }
    m_nAniIndex++;
    CWnd::OnTimer(nIDEvent);
}

void CTransparentWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
    // 模擬標題列拖曳
    PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
    CWnd::OnLButtonDown(nFlags, point);
}

void CTransparentWnd::OnRButtonDown(UINT nFlags, CPoint point)
{
    DestroyWindow();  // 右鍵關閉精靈
    CWnd::OnRButtonDown(nFlags, point);
}

應用程式啟動整合

BOOL CMyApp::InitInstance()
{
    srand((unsigned)time(NULL));

    // 單一實例檢查
    if (::FindWindow(NULL, _T("我的桌面精靈"))) return FALSE;

    CTransparentWnd* pWnd = new CTransparentWnd;
    m_pMainWnd = pWnd;

    CRect rect(0, 0, 100, 100);  // 初始大小,SetupRegion 會自動調整
    pWnd->CreateTransparent(_T("我的桌面精靈"), rect);
    pWnd->CenterWindow();
    pWnd->ShowWindow(SW_SHOW);

    return TRUE;
}

資源準備清單

  1. 位圖資源:IDB_WEST1、IDB_WEST2 (同尺寸、同透明色左上角像素)
  2. WAV 資源:IDR_WEST (類型 WAVE,資源名稱 WEST)
  3. 資源檔 .rc 需包含:WEST WAVE "west.wav"

效能優化提示

  • 區域快取: 位圖不變時勿重複 SetupRegion,可將 HRGN 快取於 std::map。
  • 雙緩衝繪圖: OnEraseBkgnd 改用記憶體 DC 完整繪製後一次 BitBlt 至螢幕。
  • 高 DPI: 專案設定 Per Monitor DPI Aware,位圖提供 100%/125%/150% 多版本或啟用 SetProcessDpiAwarenessContext。

結語

結合 SetWindowRgn 透明區域、BitBlt 位圖動畫、PlaySound 資源音效,不到 200 行代碼即可打造生動桌面精靈。此技術同樣適用於不規則視窗 UI、遊戲啟動器、桌面小工具等場景,是 MFC 開發者掌握視窗進階繪圖的經典練習專案。

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