点击时动画冻结

问题描述 投票:0回答:1

我正在尝试消除我的星星动画中的冻结。动画动画流畅,但当我单击动画区域时它会冻结。 border()、lines()、box()、SetColor() 是我自己的函数,它们不会冻结。

这是一个 WS_POPUP 窗口。我想让它动起来。我尝试处理 WM_NCHITTEST。当我拖动窗口时,它会移动,但动画会冻结。我不知道如何进一步移动以避免冻结。

#include <Windows.h>
#include <iostream>
#include "util.h"

#define IDB_GEN 101
#define IDB_EXIT 102

const int numStars = 50;
const int starSpeed = -11;

struct Star {
    int x, y;
    int speed;
};

const int NUM_STARS = 100;
Star stars[NUM_STARS];

PAINTSTRUCT ps;
HDC hdc;
RECT starfield_rc;

int WIDTH = 350;
int HEIGHT = 400;

void starfield(HDC hdc, int x, int y, RECT *rc)
{
    int width = 335;
    rc->left = x;
    rc->top = y;
    rc->right = width;
    rc->bottom = 249;

    HDC hdcMem = CreateCompatibleDC(hdc);
    HBITMAP hbmMem = CreateCompatibleBitmap(hdc, width, 215);
    HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMem);

        HBRUSH brush = CreateSolidBrush(RGB(0, 0, 0));
    FillRect(hdcMem, rc, brush);
    DeleteObject(brush);

        for (int i = 0; i < NUM_STARS; i++) {
        SetPixel(hdcMem, stars[i].x, stars[i].y, RGB(255, 255, 255));
        stars[i].x = (stars[i].x + stars[i].speed) % width;
    }
    BitBlt(hdc, x, y, width, 215, hdcMem, 0, 0, SRCCOPY);
    SelectObject(hdcMem, hbmOld);
    DeleteObject(hbmMem);
    DeleteDC(hdcMem);
}
// main function
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lparam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)
{
        for (int i = 0; i < NUM_STARS; i++) {
    stars[i].x = rand() % 340;
    stars[i].y = rand() % 249;
    stars[i].speed = rand() % 3 + 1;
}
    TCHAR appname[] = TEXT("Template");
    WNDCLASS wndclass;
    MSG msg;
    HWND hwnd;

    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hInstance = hInstance;
    wndclass.lpfnWndProc = WndProc;
    wndclass.lpszClassName = appname;
    wndclass.lpszMenuName = NULL;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;

    // check this window class is registered or not
    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("Window class is not registered"), TEXT("Error...."), MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindowEx(WS_EX_TOPMOST,appname,  // window name
                        appname,  // window text
                        WS_POPUP | WS_VISIBLE, // set POPUP window style for no border & controls
                        100,      // window position x
                        100,      // window position y
                        WIDTH,    // width
                        HEIGHT,   // height
                        NULL,
                        NULL,
                        hInstance,
                        NULL);
    // show & update created window
            SetTimer(hwnd, 1, 10, NULL); // Set a timer to trigger every 100ms
    ShowWindow(hwnd, nCmdShow);

    // get message from queue
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

// WndProc function
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    HINSTANCE hIns;
    HWND exit_button, gen_button, title, license;
    LRESULT move = NULL;

    switch (message)
    {
    case WM_CREATE:

        {
            exit_button = CreateWindow(TEXT("BUTTON"), NULL, WS_CHILD | WS_VISIBLE | BS_BITMAP,
                                       250, 360, 70, 30, hwnd, (HMENU)100, hIns, 0);
            gen_button = CreateWindow(TEXT("BUTTON"), NULL, WS_CHILD | WS_VISIBLE | BS_BITMAP,
                                      30, 360, 70, 30, hwnd, (HMENU)200, hIns, 0);
            lic = CreateWindow(TEXT("EDIT"), TEXT("LiE"), WS_CHILD | WS_VISIBLE | WS_BORDER | ES_CENTER, 10, 270, 330, 20, hwnd, (HMENU)300, hIns, 0);
            
            HBITMAP genImg = (HBITMAP)LoadImageA(GetModuleHandleA(nullptr), (LPCSTR)MAKEINTRESOURCE(IDB_GEN), IMAGE_BITMAP, 0, 0, NULL);
        HBITMAP exitImg = (HBITMAP)LoadImageA(GetModuleHandleA(nullptr), (LPCSTR)MAKEINTRESOURCE(IDB_EXIT), IMAGE_BITMAP, 0, 0, NULL);
            
            title = CreateWindow(TEXT("STATIC"), TEXT("Iamwho"), WS_CHILD | WS_VISIBLE, 150, 5, 120, 20, hwnd, 0, hIns, 0);
            
            SendMessageW(exit_button, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)exitImg);
            SendMessageW(gen_button, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)genImg);

        }

        break;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        hdc = BeginPaint(hwnd, &ps);
        // border(hdc, 0, 20, 200, 300, 1, SetColor(hdc, RGB(255, 255, 255), RGB(255,255,255)));
        lines(hdc, 5, 30, 5, 250, 2);
        lines(hdc, 345, 30, 345, 250, 2);
        starfield(hdc, 6, 31, &starfield_rc);
        box(hdc, 5, 260, 340, 90, 1, (HBRUSH)SetColor(hdc, RGB(255, 255, 255), RGB(255, 255, 255)));
        EndPaint(hwnd, &ps);
        break;
    }

    case WM_TIMER:
        InvalidateRect(hwnd, &starfield_rc, FALSE);
        break;

    case WM_COMMAND:

        switch (wParam)
        {
        case 100:
            PostQuitMessage(EXIT_SUCCESS);
            return 0;
        }

        break;
    case WM_CTLCOLORSTATIC:

    {
        hdc = (HDC)wParam;

        return (LRESULT)SetColor(hdc, RGB(255, 255, 255), RGB(0, 0, 0)); // black and white

        break;
    }

    case WM_CTLCOLOREDIT:

    {
        hdc = (HDC)wParam;

        return (LRESULT)SetColor(hdc, RGB(255, 255, 255), RGB(0, 0, 0)); // black and white

        break;
    }

   case WM_NCHITTEST:
        RECT rc;
        POINT pt;

        GetCursorPos(&pt);

        GetWindowRect(hwnd, &rc);
        rc.bottom = rc.bottom - 200;

        // if cursor position is within top layered drawn rectangle then
        // set move to HTCAPTION for moving the window from its client
        if (pt.x <= rc.right && pt.x >= rc.left && pt.y <= rc.bottom && pt.y >= rc.top)
        {
            move = DefWindowProc(hwnd, message, wParam, lParam);
            if (move == HTCLIENT)
            {
                move = HTCAPTION;
            }
        }
        
        return move;

        break;


    case WM_DESTROY:
        PostQuitMessage(EXIT_SUCCESS);
        return 0;
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}
c++ c winapi gdi
1个回答
0
投票

拖动窗口会运行单独的模式消息循环,直到拖动完成(发生这种情况时,您将收到

WM_ENTERSIZEMOVE
WM_EXITSIZEMOVE
消息)。通过模拟“标题”上的鼠标活动,让操作系统拖动您的窗口 *,您将让拖动的内部消息循环阻止您的消息循环,因此您不会在消息队列中收到正常的
WM_PAINT
消息,直到拖动完成。

* 处理

WM_NCHITTEST
您现在的方式并不是允许拖动无标题窗口的最佳方式!一方面,您忽略了
WM_NCHITTEST
提供的鼠标坐标。另一方面,您将为动画区域之外的每个坐标返回
HTNOWHERE
,而不是返回
DefWindowProc()
通常报告的内容。相反,您应该处理
WM_LBUTTONDOWN
消息并让它调用
ReleaseCapture()
,然后向您的窗口发送一条
WM_SYSCOMAND(SC_DRAGMOVE)
消息(其中
SC_DRAGMOVE=$F012
)。

由于您已经有一个计时器(它将在模式消息循环中继续正常运行),因此您可以让它在

UpdateWindow()
之后调用
InvalidateRect()
来强制在拖动状态下立即非排队
WM_PAINT
消息,例如:

...

bool isSizingOrMoving = false;

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    ...
    switch (message)
    {
        ...

        case WM_ENTERSIZEMOVE:
            isSizingOrMoving = true;
            break;
 
        case WM_EXITSIZEMOVE:
            isSizingOrMoving = false;
            break;

        case WM_TIMER:
            InvalidateRect(hwnd, &starfield_rc, FALSE);
            if (isSizingOrMoving)
                UpdateWindow(hwnd);
            break;

        case WM_NCHITTEST: {
            POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };

            RECT rc;
            GetWindowRect(hwnd, &rc);
            rc.bottom -= 200;

            // if cursor position is within top layered drawn rectangle then
            // set move to HTCAPTION for moving the window from its client
            if (PtInRect(&rc, pt))
            {
                LRESULT move = DefWindowProc(hwnd, message, wParam, lParam);
                return (move == HTCLIENT) ? HTCAPTION : move;
            }        

            break;
        }

        /*
        case WM_LBUTTONDOWN: {
            POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };

            RECT rc;
            GetClientRect(hwnd, &rc);
            rc.bottom -= 200;

            if (PtInRect(&rc, pt))
            {
                ReleaseCapture();
                const WPARAM SC_DRAGMOVE = $F012;
                SendMessage(hwnd, WM_SYSCOMAND, SC_DRAGMOVE, 0);
                return 0;
            }        

            break;
        }
        */

        ...
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}
© www.soinside.com 2019 - 2024. All rights reserved.