Skip to content

Commit 825330a

Browse files
Add files via upload
1 parent 3d02477 commit 825330a

File tree

1 file changed

+217
-0
lines changed

1 file changed

+217
-0
lines changed

code.txt

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
#include <windows.h>
2+
3+
#define ID_TIMER 1
4+
#define STRMAXLEN 25 //一个显示列的最大长度
5+
#define STRMINLEN 8 //一个显示列的最小长度
6+
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
7+
8+
//////////////////////////////////////////////////////////////////
9+
10+
typedef struct tagCharChain //整个当作屏幕的一个显示列,这是个双向列表
11+
{
12+
struct tagCharChain *prev; //链表的前个元素
13+
TCHAR ch; //一个显示列中的一个字符
14+
struct tagCharChain *next; //链表的后个元素
15+
}CharChain, *pCharChain;
16+
17+
typedef struct tagCharColumn
18+
{
19+
CharChain *head, *current, *point;
20+
int x, y, iStrLen; //显示列的开始显示的x,y坐标,iStrLen是这个列的长度
21+
int iStopTimes, iMustStopTimes; //已经停滞的次数和必须停滞的次数,必须停滞的次数是随机的,其实就是每列的速度,速度=计时器大小*iMustStopTimes。
22+
}CharColumn, *pCharColumn;
23+
24+
int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
25+
{
26+
static TCHAR szAppName[] = TEXT("matrix");
27+
HWND hwnd;//窗口句柄
28+
MSG msg;//
29+
WNDCLASS wndclass;
30+
wndclass.style = CS_HREDRAW | CS_VREDRAW;
31+
wndclass.lpfnWndProc = WndProc;
32+
wndclass.cbClsExtra = 0;
33+
wndclass.cbWndExtra = 0;
34+
wndclass.hInstance = hInstance;
35+
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
36+
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
37+
wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
38+
wndclass.lpszMenuName = NULL;
39+
wndclass.lpszClassName = szAppName;
40+
41+
if (!RegisterClass(&wndclass))
42+
{
43+
MessageBox(NULL, TEXT("此程序必须运行在NT下!"), szAppName, MB_ICONERROR);
44+
return 0;
45+
}
46+
47+
hwnd = CreateWindow(szAppName, NULL, WS_DLGFRAME | WS_THICKFRAME | WS_POPUP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
48+
NULL, NULL, hInstance, NULL);
49+
50+
ShowWindow(hwnd, SW_SHOWMAXIMIZED); //最大化显示
51+
UpdateWindow(hwnd);
52+
ShowCursor(FALSE); //隐藏鼠标光标
53+
srand((int)GetCurrentTime()); //初始化随机数发生器
54+
55+
while (GetMessage(&msg, NULL, 0, 0))
56+
{
57+
TranslateMessage(&msg);
58+
DispatchMessage(&msg);
59+
}
60+
61+
ShowCursor(TRUE); //显示鼠标光标
62+
return msg.wParam;
63+
}
64+
65+
TCHAR randomChar() //随机字符产生函数
66+
{
67+
return (TCHAR)(rand() % (126 - 33) + 33); //33到126之间
68+
}
69+
70+
int init(CharColumn *cc, int cyScreen, int x) //初始化,cyScreen屏幕高度
71+
{
72+
int j;
73+
cc->iStrLen = rand() % (STRMAXLEN - STRMINLEN) + STRMINLEN; //显示列的长度
74+
cc->x = x + 3; //显示列的开始显示的x坐标
75+
cc->y = rand() % 3 ? rand() % cyScreen : 0; //显示列的开始显示的y坐标
76+
cc->iMustStopTimes = rand() % 6;//cc->iMustStopTimes = rand()%(200-100)+100;读者可以把速度调慢点,我调的是1s~2s,注意此时计时器需要调为10ms
77+
cc->iStopTimes = 0;
78+
cc->head = cc->current = (pCharChain)calloc(cc->iStrLen, sizeof(CharChain)); //分配连续iStrLen个CharChain类型空间
79+
80+
for (j = 0; j<cc->iStrLen - 1; j++)//遍历 iStrLen-1 次
81+
{
82+
cc->current->prev = cc->point; //cc->point一个显示列的前个元素
83+
cc->current->ch = '\0';
84+
cc->current->next = cc->current + 1; //cc->current+1一个显示列的后个元素
85+
cc->point = cc->current++; //cc->point = cc->current; cc->current++;
86+
}
87+
cc->current->prev = cc->point; //最后一个节点
88+
cc->current->ch = '\0';
89+
cc->current->next = cc->head;
90+
cc->head->prev = cc->current; //头节点的前一个为此链的最后一个元素
91+
92+
cc->current = cc->point = cc->head; //free掉申请的内存要用current当参数
93+
cc->head->ch = randomChar(); //对链表头的元素填充
94+
95+
return 0;
96+
}
97+
98+
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
99+
{
100+
HDC hdc;
101+
102+
//j 为一个显示链中除链表头外的在屏幕上显示的y坐标,temp绿色过度到黑色之用
103+
//ctn 用来确定一个显示链是否向下前进,如果等待次数超过必须等待的次数,ctn就代表要向下前进
104+
int i, j, temp, ctn;
105+
static HDC hdcMem;
106+
HFONT hFont;
107+
static HBITMAP hBitmap;
108+
static int cxScreen, cyScreen; //屏幕的宽度 高度.
109+
static int iFontWidth = 10, iFontHeight = 15, iColumnCount; //字体的宽度 高度, 列数
110+
static CharColumn *ccChain;
111+
112+
switch (message)
113+
{
114+
//系统给消息处理程序发送的消息,每次窗口创建,有且只有1个WM_CREATE消息,所以只运行1次,可以在这里初始化滚动条,获取字体宽度、高度,创建子窗口、控件等等
115+
case WM_CREATE:
116+
cxScreen = GetSystemMetrics(SM_CXSCREEN); //屏幕宽度
117+
cyScreen = GetSystemMetrics(SM_CYSCREEN); //屏幕高度
118+
119+
//hwnd调用,计时器命名为ID_TIMER,时间间隔10ms。给读者一个小技巧,因为10ms太小,读者难免很难发现玄机,所以建议读者在理解代码时将时间改为1000ms
120+
SetTimer(hwnd, ID_TIMER, 10, NULL);
121+
hdc = GetDC(hwnd);
122+
hdcMem = CreateCompatibleDC(hdc);
123+
hBitmap = CreateCompatibleBitmap(hdc, cxScreen, cyScreen);
124+
SelectObject(hdcMem, hBitmap);
125+
ReleaseDC(hwnd, hdc);
126+
127+
//创建字体
128+
hFont = CreateFont(iFontHeight, iFontWidth - 5, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
129+
DRAFT_QUALITY, FIXED_PITCH | FF_SWISS, TEXT("Fixedsys"));
130+
131+
SelectObject(hdcMem, hFont);
132+
DeleteObject(hFont);
133+
134+
SetBkMode(hdcMem, TRANSPARENT); //设置背景模式为透明
135+
iColumnCount = cxScreen / (iFontWidth * 3 / 2); //屏幕所显示字母雨的列数
136+
ccChain = (pCharColumn)calloc(iColumnCount, sizeof(CharColumn));
137+
138+
for (i = 0; i < iColumnCount; i++)
139+
init(ccChain + i, cyScreen, (iFontWidth * 3 / 2)*i);
140+
return 0;
141+
142+
//计时器间隔10ms,该消息每隔10ms触发一次
143+
case WM_TIMER:
144+
hdc = GetDC(hwnd);
145+
PatBlt(hdcMem, 0, 0, cxScreen, cyScreen, BLACKNESS); //将内存设备映像刷成黑色,不理解含义可以注释此句再运行,对比一下即可
146+
147+
for (i = 0; i<iColumnCount; i++)
148+
{
149+
ctn = (ccChain + i)->iStopTimes++ >(ccChain + i)->iMustStopTimes;
150+
151+
(ccChain + i)->point = (ccChain + i)->head; //point用于遍历整个显示列
152+
153+
SetTextColor(hdcMem, RGB(255, 255, 255));//第一个字符显示为白色
154+
TextOut(hdcMem, (ccChain + i)->x, (ccChain + i)->y, &((ccChain + i)->point->ch), 1);
155+
156+
j = (ccChain + i)->y;
157+
(ccChain + i)->point = (ccChain + i)->point->next;
158+
//遍历整个显示列,将这个显示列里的字符从下往上显示
159+
temp = 0; //temp绿色过度到黑色之用
160+
161+
//注意第二个判断条件,init函数里除了head节点,其他节点都是'\0',正是第二个判断才给我们运行起初字母是一个一个增多的效果,读者可以换成下面的句子运行看看
162+
//while ((ccChain + i)->point != (ccChain + i)->head)
163+
while ((ccChain + i)->point != (ccChain + i)->head && (ccChain + i)->point->ch)
164+
{
165+
SetTextColor(hdcMem, RGB(0, 255 - (255 * (temp++) / (ccChain + i)->iStrLen), 0));
166+
TextOut(hdcMem, (ccChain + i)->x, j -= iFontHeight, &((ccChain + i)->point->ch), 1);
167+
(ccChain + i)->point = (ccChain + i)->point->next;
168+
}
169+
170+
if (ctn)
171+
(ccChain + i)->iStopTimes = 0;
172+
else
173+
continue;
174+
175+
(ccChain + i)->y += iFontHeight; //下次开始显示的y坐标 为当前的y坐标加上一个字符的高度
176+
177+
//如果开始显示的y坐标减去 整个显示列的长度超过了屏幕的高度
178+
if ((ccChain + i)->y - (ccChain + i)->iStrLen*iFontHeight > cyScreen)
179+
{
180+
free((ccChain + i)->current);
181+
init(ccChain + i, cyScreen, (iFontWidth * 3 / 2)*i);
182+
}
183+
184+
//链表的头 为此链表的前个元素,因为下次开始显示的时候 就相当于在整个显示列的开头添加个元素,然后再开始往上显示
185+
(ccChain + i)->head = (ccChain + i)->head->prev;
186+
(ccChain + i)->head->ch = randomChar();
187+
}
188+
189+
BitBlt(hdc, 0, 0, cxScreen, cyScreen, hdcMem, 0, 0, SRCCOPY);
190+
ReleaseDC(hwnd, hdc);
191+
return 0;
192+
193+
case WM_RBUTTONDOWN:
194+
KillTimer(hwnd, ID_TIMER);
195+
return 0;
196+
197+
case WM_RBUTTONUP:
198+
SetTimer(hwnd, ID_TIMER, 10, NULL);
199+
return 0;
200+
201+
//处理善后工作
202+
case WM_KEYDOWN: //任意键退出
203+
case WM_LBUTTONDOWN: //点击鼠标退出
204+
case WM_DESTROY:
205+
KillTimer(hwnd, ID_TIMER);
206+
DeleteObject(hBitmap);
207+
DeleteDC(hdcMem);
208+
209+
for (i = 0; i<iColumnCount; i++)
210+
free((ccChain + i)->current);
211+
free(ccChain);
212+
PostQuitMessage(0);
213+
return 0;
214+
}
215+
216+
return DefWindowProc(hwnd, message, wParam, lParam);
217+
}

0 commit comments

Comments
 (0)