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

MFC 對話框動畫彈出與淡出消隱:SetTimer + MoveWindow 逐幀縮放實作

     編輯:VC 教學

效果展示

對話框啟動時:從中心點 (0x0) 逐漸放大至完整尺寸 (動畫彈出)。
對話框關閉時:從完整尺寸逐漸縮小至中心點消失 (動畫消隱)。

核心原理

  • 使用 SetTimer 定時器驅動動畫幀 (如 10ms/幀,100 FPS)。
  • 每幀呼叫 MoveWindow 調整視窗大小與位置,保持視窗中心點固定。
  • 寬高分別以固定步長 (dx, dy) 增減,達到目標尺寸後殺死定時器。

完整實作代碼

1. 成員變數宣告 (Header)


class CAnimDlg : public CDialog
{
    // ...
protected:
    CPoint m_ptCenter;      // 螢幕中心點 (視窗中心維持不變)
    int m_nTargetW, m_nTargetH; // 目標寬高
    int m_nStepW, m_nStepH;     // 每幀寬高變化量
    int m_nCloseStepW, m_nCloseStepH; // 關閉動畫步長
    bool m_bAnimating = false; // 動畫狀態
    enum { TIMER_OPEN = 1, TIMER_CLOSE = 2 };
};

2. OnInitDialog:初始化幾何參數、啟動展開動畫


BOOL CAnimDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // 1. 取得對話框設計尺寸
    CRect dlgRect;
    GetWindowRect(&dlgRect);
    m_nTargetW = dlgRect.Width();
    m_nTargetH = dlgRect.Height();

    // 2. 計算螢幕中心點
    CRect deskRect;
    GetDesktopWindow()->GetWindowRect(&deskRect);
    m_ptCenter = deskRect.CenterPoint();

    // 3. 設定動畫步長 (可根據需求調整)
    m_nStepW = 20;  // 每幀寬度增加 20 像素
    m_nStepH = 15;  // 每幀高度增加 15 像素
    m_nCloseStepW = 25; // 關閉時收縮較快
    m_nCloseStepH = 20;

    // 4. 將視窗移至中心點,尺寸設為 0 (隱藏)
    MoveWindow(
        m_ptCenter.x, m_ptCenter.y, // 左上角 = 中心點 (寬高為 0 時即為中心)
        0, 0,
        TRUE // 重繪
    );

    // 5. 啟動展開動畫定時器
    SetTimer(TIMER_OPEN, 10, NULL); // 10ms ~ 100 FPS
    m_bAnimating = true;

    return TRUE; // 讓系統設定焦點
}

3. OnTimer:雙向動畫邏輯


void CAnimDlg::OnTimer(UINT_PTR nIDEvent)
{
    if (!m_bAnimating)
    {
        CDialog::OnTimer(nIDEvent);
        return;
    }

    CRect curRect;
    GetWindowRect(&curRect);
    CRect deskRect;
    GetDesktopWindow()->GetWindowRect(&deskRect);
    CPoint center = deskRect.CenterPoint();

    if (nIDEvent == TIMER_OPEN) // 展開動畫
    {
        int newW = curRect.Width() + m_nStepW;
        int newH = curRect.Height() + m_nStepH;

        // 限制不超過目標尺寸
        if (newW >= m_nTargetW) newW = m_nTargetW;
        if (newH >= m_nTargetH) newH = m_nTargetH;

        // 計算新左上角 (保持中心點不變)
        int newX = center.x - newW / 2;
        int newY = center.y - newH / 2;

        MoveWindow(newX, newY, newW, newH, TRUE);

        // 達到目標尺寸,停止動畫
        if (newW == m_nTargetW && newH == m_nTargetH)
        {
            KillTimer(TIMER_OPEN);
            m_bAnimating = false;
        }
    }
    else if (nIDEvent == TIMER_CLOSE) // 關閉動畫
    {
        int newW = curRect.Width() - m_nCloseStepW;
        int newH = curRect.Height() - m_nCloseStepH;

        // 限制不小於 0
        if (newW <= 0) newW = 0;
        if (newH <= 0) newH = 0;

        int newX = center.x - newW / 2;
        int newY = center.y - newH / 2;

        MoveWindow(newX, newY, newW, newH, TRUE);

        // 縮至 0,真正關閉對話框
        if (newW == 0 && newH == 0)
        {
            KillTimer(TIMER_CLOSE);
            m_bAnimating = false;
            CDialog::OnOK(); // 或 OnCancel(),觸發正常關閉流程
        }
    }

    CDialog::OnTimer(nIDEvent);
}

4. 觸發關閉動畫 (覆寫 OnOK/OnCancel 或自訂按鈕)


void CAnimDlg::OnOK()
{
    // 若正在展開動畫,先停止
    if (m_bAnimating)
    {
        KillTimer(TIMER_OPEN);
    }

    // 啟動關閉動畫
    SetTimer(TIMER_CLOSE, 10, NULL);
    m_bAnimating = true;
    // 不呼叫 CDialog::OnOK(),等動畫結束再呼叫
}

void CAnimDlg::OnCancel()
{
    OnOK(); // 同樣觸發關閉動畫
}

進階優化:平滑插值 (Easing) 效果

固定步長會有「機械感」。使用緩動函數讓動畫更自然:


// 緩動函數:Ease Out Quad (快開始、慢結束)
float EaseOutQuad(float t) { return 1 - (1 - t) * (1 - t); }

// OnTimer 中計算進度
float progress = static_cast(curRect.Width()) / m_nTargetW; // 0.0 ~ 1.0
float eased = EaseOutQuad(progress);
int newW = static_cast(m_nTargetW * eased);
// 同理計算 newH

常見問題與解決

問題原因解決
動畫閃爍/殘影MoveWindow 重繪過於頻繁、背景擦除確保對話框類別風格含 CS_DBLCLKS,或處理 WM_ERASEBKGND 返回 TRUE
關閉動畫後程式未結束OnOK 中未呼叫 CDialog::OnOK()動畫結束時 (newW==0) 呼叫 CDialog::OnOK()
多螢幕中心點錯誤GetDesktopWindow() 範圍為虛擬螢幕使用 MonitorFromWindow + GetMonitorInfo 取得當前螢幕工作區
模態對話框 DoModal 期間無法動畫DoModal 內部訊息迴圈阻塞此技巧適用於 Create() 非模態對話框,或模態對話框在 OnInitDialog 內啟動定時器 (需確保訊息泵運行)

適用場景與限制

  • 適用:啟動畫面、工具面板、通知氣泡、非模態工具對話框。
  • 不適用:嚴肅業務對話框 (使用者期望即時響應)、遠端桌面/虛擬機 (動畫延遲嚴重)、高 DPI 縮放環境 (需配合 DPI 感知調整步長)。

結語

對話框動畫彈出/消隱透過 SetTimer + MoveWindow 即可實現,核心在於保持中心點固定、分步調整寬高、定時器驅動狀態機。加入緩動函數可大幅提升視覺質感。現代 Windows 開發 (WPF/UWP/WinUI) 內建動畫系統 (Storyboard/Composition API),MFC 專案若需豐富動畫,建議引入 DirectComposition 或遷移至現代 UI 框架。

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