[toc]

鼠标控制图形的移动

1.让图形随着鼠标位置移动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <windows.h>

// 定义全局变量来存储矩形的坐标和大小
int rectX = 50; // 矩形左上角的X坐标
int rectY = 50; // 矩形左上角的Y坐标
int rectWidth = 50; // 矩形的宽度
int rectHeight = 30; // 矩形的高度

// 定义标志来跟踪矩形是否被选中
BOOL isRectSelected = FALSE;
POINT offset; // 用于存储鼠标点击时与矩形左上角的偏移量

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// 清除窗口背景
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

// 绘制矩形
Rectangle(hdc, rectX, rectY, rectX + rectWidth, rectY + rectHeight);

EndPaint(hwnd, &ps);
} break;

case WM_LBUTTONDOWN: { // 处理鼠标左键点击事件
// 获取鼠标点击的位置
int mouseX = LOWORD(lParam);
int mouseY = HIWORD(lParam);

// 检查点击位置是否在矩形内
if (mouseX >= rectX && mouseX <= rectX + rectWidth &&
mouseY >= rectY && mouseY <= rectY + rectHeight) {
// 如果点击在矩形内,选中矩形并计算偏移量
isRectSelected = TRUE;
offset.x = mouseX - rectX;
offset.y = mouseY - rectY;
}

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
} break;

case WM_LBUTTONUP: { // 处理鼠标左键释放事件
isRectSelected = FALSE; // 释放鼠标时取消选中
} break;


case WM_MOUSEMOVE: { // 处理鼠标移动事件
if (isRectSelected) {
// 获取当前鼠标位置
int mouseX = LOWORD(lParam);
int mouseY = HIWORD(lParam);

// 更新矩形的位置
rectX = mouseX - offset.x;
rectY = mouseY - offset.y;

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
}

// 设置鼠标跟踪
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
TrackMouseEvent(&tme);
} break;

case WM_MOUSELEAVE: { // 处理鼠标离开事件
isRectSelected = FALSE; // 鼠标离开时取消选中
InvalidateRect(hwnd, NULL, TRUE); // 触发重绘窗口
} break;

case WM_DESTROY:
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const char CLASS_NAME[] = "Sample Window Class";

WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Rectangle Control Example",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
NULL, NULL, hInstance, NULL
);

ShowWindow(hwnd, nShowCmd);

// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}

2.解决鼠标位置延迟,使用定时器来检查鼠标位置而不是完全依赖于 WM_MOUSELEAVE 消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <windows.h>

// 定义全局变量来存储矩形的坐标和大小
int rectX = 50; // 矩形左上角的X坐标
int rectY = 50; // 矩形左上角的Y坐标
int rectWidth = 50; // 矩形的宽度
int rectHeight = 30; // 矩形的高度

// 定义标志来跟踪矩形是否被选中
BOOL isRectSelected = FALSE;
POINT offset; // 用于存储鼠标点击时与矩形左上角的偏移量

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// 清除窗口背景
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

// 绘制矩形
Rectangle(hdc, rectX, rectY, rectX + rectWidth, rectY + rectHeight);

EndPaint(hwnd, &ps);
} break;

case WM_LBUTTONDOWN: { // 处理鼠标左键点击事件
// 获取鼠标点击的位置
int mouseX = LOWORD(lParam);
int mouseY = HIWORD(lParam);

// 检查点击位置是否在矩形内
if (mouseX >= rectX && mouseX <= rectX + rectWidth &&
mouseY >= rectY && mouseY <= rectY + rectHeight) {
// 如果点击在矩形内,选中矩形并计算偏移量
isRectSelected = TRUE;
offset.x = mouseX - rectX;
offset.y = mouseY - rectY;
}

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
} break;

case WM_LBUTTONUP: { // 处理鼠标左键释放事件
isRectSelected = FALSE; // 释放鼠标时取消选中
} break;

case WM_MOUSEMOVE: {
if (isRectSelected) {
// 获取当前鼠标位置
int mouseX = LOWORD(lParam);
int mouseY = HIWORD(lParam);

// 更新矩形的位置
rectX = mouseX - offset.x;
rectY = mouseY - offset.y;

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
}

// 设置鼠标跟踪
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
TrackMouseEvent(&tme);

// 启动定时器
SetTimer(hwnd, 1, 100, NULL); // 每100毫秒检查一次
} break;

case WM_TIMER: {
// 检查鼠标是否在窗口内
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hwnd, &pt);
RECT clientRect;
GetClientRect(hwnd, &clientRect);
if (pt.x < 0 || pt.x >= clientRect.right || pt.y < 0 || pt.y >= clientRect.bottom) {
isRectSelected = FALSE; // 鼠标离开窗口
InvalidateRect(hwnd, NULL, TRUE);
}
} break;

case WM_MOUSELEAVE: { // 处理鼠标离开事件
isRectSelected = FALSE; // 鼠标离开时取消选中
InvalidateRect(hwnd, NULL, TRUE); // 触发重绘窗口
} break;

case WM_DESTROY:
KillTimer(hwnd, 1); // 清理定时器
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const char CLASS_NAME[] = "Sample Window Class";

WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Rectangle Control Example",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
NULL, NULL, hInstance, NULL
);

ShowWindow(hwnd, nShowCmd);

// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}

3.鼠标点击窗口任意位置,让图形瞬间移动到对应的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <windows.h>

// 定义全局变量来存储矩形的坐标和大小
int rectX = 50; // 矩形左上角的X坐标
int rectY = 50; // 矩形左上角的Y坐标
int rectWidth = 50; // 矩形的宽度
int rectHeight = 30; // 矩形的高度

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// 清除窗口背景
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

// 绘制矩形
Rectangle(hdc, rectX, rectY, rectX + rectWidth, rectY + rectHeight);

EndPaint(hwnd, &ps);
} break;

case WM_LBUTTONDOWN: { // 处理鼠标左键点击事件
// 获取鼠标点击的位置
int mouseX = LOWORD(lParam);
int mouseY = HIWORD(lParam);

// 将矩形移动到鼠标点击的位置
rectX = mouseX;
rectY = mouseY;

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
} break;

case WM_DESTROY:
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const char CLASS_NAME[] = "Sample Window Class";

WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Rectangle Move Example",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
NULL, NULL, hInstance, NULL
);

ShowWindow(hwnd, nShowCmd);

// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}

4.鼠标点击窗口任意位置,让图形逐步移动到对应的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <windows.h>
#include <math.h>

// 定义全局变量来存储矩形的坐标和大小
int rectX = 50; // 矩形左上角的X坐标
int rectY = 50; // 矩形左上角的Y坐标
int rectWidth = 50; // 矩形的宽度
int rectHeight = 30; // 矩形的高度

// 目标位置
int targetX = 50;
int targetY = 50;

// 移动步长
const int stepSize = 5; // 每次移动的像素数

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// 清除窗口背景
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

// 绘制矩形
Rectangle(hdc, rectX, rectY, rectX + rectWidth, rectY + rectHeight);

EndPaint(hwnd, &ps);
} break;

case WM_LBUTTONDOWN: { // 处理鼠标左键点击事件
// 获取鼠标点击的位置
targetX = LOWORD(lParam);
targetY = HIWORD(lParam);

// 启动定时器
SetTimer(hwnd, 1, 30, NULL); // 每30毫秒更新一次
} break;

case WM_TIMER: {
// 计算矩形移动的方向
if (rectX < targetX) {
rectX += stepSize;
if (rectX > targetX) rectX = targetX; // 确保不超过目标位置
}
else if (rectX > targetX) {
rectX -= stepSize;
if (rectX < targetX) rectX = targetX; // 确保不超过目标位置
}

if (rectY < targetY) {
rectY += stepSize;
if (rectY > targetY) rectY = targetY; // 确保不超过目标位置
}
else if (rectY > targetY) {
rectY -= stepSize;
if (rectY < targetY) rectY = targetY; // 确保不超过目标位置
}

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);

// 如果矩形到达目标位置,停止定时器
if (rectX == targetX && rectY == targetY) {
KillTimer(hwnd, 1);
}
} break;

case WM_DESTROY:
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const char CLASS_NAME[] = "Sample Window Class";

WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Smooth Rectangle Move Example",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
NULL, NULL, hInstance, NULL
);

ShowWindow(hwnd, nShowCmd);

// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}

5.鼠标点击窗口任意位置,让图形逐步移动使鼠标点击的位置作为矩形移动后的中心点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <windows.h>

// 定义全局变量来存储矩形的坐标和大小
int rectX = 50; // 矩形左上角的X坐标
int rectY = 50; // 矩形左上角的Y坐标
int rectWidth = 50; // 矩形的宽度
int rectHeight = 30; // 矩形的高度

// 目标位置
int targetX = 50; // 目标中心点X坐标
int targetY = 50; // 目标中心点Y坐标

// 移动步长
const int stepSize = 5; // 每次移动的像素数

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// 清除窗口背景
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

// 绘制矩形
Rectangle(hdc, rectX, rectY, rectX + rectWidth, rectY + rectHeight);

EndPaint(hwnd, &ps);
} break;

case WM_LBUTTONDOWN: { // 处理鼠标左键点击事件
// 获取鼠标点击的位置
targetX = LOWORD(lParam);
targetY = HIWORD(lParam);

// 启动定时器
SetTimer(hwnd, 1, 30, NULL); // 每30毫秒更新一次
} break;

case WM_TIMER: {
// 计算矩形的中心点
int centerX = rectX + rectWidth / 2;
int centerY = rectY + rectHeight / 2;

// 计算矩形移动的方向
if (centerX < targetX) {
rectX += stepSize;
if (centerX + stepSize > targetX) rectX = targetX - rectWidth / 2; // 确保不超过目标位置
}
else if (centerX > targetX) {
rectX -= stepSize;
if (centerX - stepSize < targetX) rectX = targetX - rectWidth / 2; // 确保不超过目标位置
}

if (centerY < targetY) {
rectY += stepSize;
if (centerY + stepSize > targetY) rectY = targetY - rectHeight / 2; // 确保不超过目标位置
}
else if (centerY > targetY) {
rectY -= stepSize;
if (centerY - stepSize < targetY) rectY = targetY - rectHeight / 2; // 确保不超过目标位置
}

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);

// 如果矩形的中心点到达目标位置,停止定时器
if (centerX == targetX && centerY == targetY) {
KillTimer(hwnd, 1);
}
} break;

case WM_DESTROY:
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const char CLASS_NAME[] = "Sample Window Class";

WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Smooth Rectangle Move Example",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
NULL, NULL, hInstance, NULL
);

ShowWindow(hwnd, nShowCmd);

// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}