You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击/拖拽WS_POPUP窗口时星星动画卡顿问题求助

星星动画拖拽/点击卡顿问题优化方案

问题根源

拖拽WS_POPUP窗口时,系统会持续发送窗口移动相关消息,占用主线程资源,导致定时器绘制消息被延迟。同时当前代码每次绘制星星都重复创建/销毁内存DC和位图,加重了主线程负载,放大了卡顿现象。

具体优化措施

1. 预创建离屏绘制缓冲区

避免每次绘制都重复创建内存DC、位图等资源,将这些资源改为全局变量,在窗口创建时初始化,销毁时释放:

  • 添加全局资源变量:
HDC hdcMem = NULL;
HBITMAP hbmMem = NULL;
HBITMAP hbmOld = NULL;
const int STARFIELD_WIDTH = 335;
const int STARFIELD_HEIGHT = 215;
  • 在WM_CREATE中初始化缓冲区:
case WM_CREATE:
    // ... 原有控件创建代码
    HDC hdc = GetDC(hwnd);
    hdcMem = CreateCompatibleDC(hdc);
    hbmMem = CreateCompatibleBitmap(hdc, STARFIELD_WIDTH, STARFIELD_HEIGHT);
    hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMem);
    ReleaseDC(hwnd, hdc);

    // 初始化缓冲区为黑色背景
    HBRUSH brush = CreateSolidBrush(RGB(0, 0, 0));
    RECT rcInit = {0, 0, STARFIELD_WIDTH, STARFIELD_HEIGHT};
    FillRect(hdcMem, &rcInit, brush);
    DeleteObject(brush);
    break;
  • 在WM_DESTROY中释放资源:
case WM_DESTROY:
    if (hdcMem) {
        SelectObject(hdcMem, hbmOld);
        DeleteObject(hbmMem);
        DeleteDC(hdcMem);
    }
    PostQuitMessage(EXIT_SUCCESS);
    return 0;

2. 优化星星绘制逻辑

修改starfield函数,复用缓冲区,只清除上一帧星星的位置而非整个区域,减少绘制开销:

void starfield(HDC hdc, int x, int y, RECT *rc)
{
    rc->left = x;
    rc->top = y;
    rc->right = x + STARFIELD_WIDTH;
    rc->bottom = y + STARFIELD_HEIGHT;

    // 清除上一帧的星星位置
    HBRUSH brush = CreateSolidBrush(RGB(0, 0, 0));
    for (int i = 0; i < NUM_STARS; i++) {
        RECT starRect = {stars[i].x, stars[i].y, stars[i].x + 1, stars[i].y + 1};
        FillRect(hdcMem, &starRect, brush);
    }
    DeleteObject(brush);

    // 更新星星位置并绘制新位置
    for (int i = 0; i < NUM_STARS; i++) {
        stars[i].x = (stars[i].x + stars[i].speed) % STARFIELD_WIDTH;
        if (stars[i].x < 0) stars[i].x += STARFIELD_WIDTH; // 处理speed为负的边界情况
        SetPixel(hdcMem, stars[i].x, stars[i].y, RGB(255, 255, 255));
    }

    BitBlt(hdc, x, y, STARFIELD_WIDTH, STARFIELD_HEIGHT, hdcMem, 0, 0, SRCCOPY);
}

3. 简化WM_NCHITTEST处理

减少不必要的系统调用,直接使用消息参数中的坐标判断拖拽区域:

case WM_NCHITTEST:
    LRESULT hitResult = DefWindowProc(hwnd, message, wParam, lParam);
    if (hitResult == HTCLIENT) {
        POINT pt = {LOWORD(lParam), HIWORD(lParam)};
        RECT rc;
        GetWindowRect(hwnd, &rc);
        rc.bottom = rc.bottom - 200;
        if (PtInRect(&rc, pt)) {
            hitResult = HTCAPTION;
        }
    }
    return hitResult;
    break;

4. 调整定时器频率

将定时器间隔从10ms改为16ms(约60fps),降低主线程绘制压力:

SetTimer(hwnd, 1, 16, NULL);

额外建议

  • 如果SetPixel仍然性能不足,可以改用PolylineTo或直接操作位图内存(通过GetDIBits/SetDIBits)批量绘制星星,进一步提升效率。
  • 确保窗口类样式去掉CS_HREDRAW | CS_VREDRAW,因为我们只需要局部刷新星星区域,全局重绘会增加不必要的开销:
wndclass.style = 0; // 替换原有的CS_HREDRAW | CS_VREDRAW

内容的提问来源于stack exchange,提问作者iamwho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 19:24:57