注:本文為 “C 語言圖形編程” 相關文章合輯。
略作重排,如有內容異常,請看原文。
C 語言圖形化界面——含圖形、按鈕、鼠標、進度條等部件制作(帶詳細代碼、講解及注釋)
非線性光學元件于 2020-02-15 09:42:37 發布
0. 引言
在 CSDN 上,許多關于 C 程序圖形化界面的介紹存在代碼繁瑣難解、不便調試修改或講解不夠詳細的問題。本文提供的代碼簡單、易于移植且容易理解,希望對急需使用 C 語言制作圖形化界面的讀者有所幫助。
對于不熟悉 EasyX 的讀者,只需 10 分鐘即可上手,而它可能為您節省 3 個小時甚至更多的時間。
關于 EasyX 的簡單應用,可參考作者之前關于 C 程序可視化的博文:C語言繪圖實驗-CSDN博客(附后)。
本文的講解循序漸進,讀者應重點關注每個步驟的理解以及兩步之間代碼的變化。
1. 素材準備
-
EasyX 的下載鏈接如下(本文使用的版本是 2014 冬至版):EasyX Graphics Library for C++。使用 EasyX 需注意其兼容的編譯器(下載的幫助文件中有說明),不同版本的 EasyX 兼容的編譯器不同,但均與 Visual C++6 兼容(與字符編碼有關)。本文以 Visual C++6 編譯器為例編寫代碼。
-
EasyX 的最新英文幫助文檔鏈接(下載 2014 冬至版會自帶中文幫助文檔):EasyX 文檔 - Basic introduction
-
如果成功下載了 EasyX 2014 冬至版,解壓后將頭文件(
easyx.h
和graphics.h
)和lib
文件(amd64
)分別放在 VC 文件夾默認的include
文件夾和lib
文件夾中。右鍵點擊 VC 程序,選擇“打開文件所在位置”,然后找到 MFC 文件夾。以下是兩個文件夾的位置截圖:
-
建議將編譯的 C 文件以
.cpp
后綴保存。
2. 編程
2.1 創建界面
創建一個 480×360 的窗口,需要使用 initgraph()
函數。以下是代碼示例:
#include <graphics.h> // 引用圖形庫頭文件
#include <conio.h>
#include <stdio.h>
#include <windows.h> // 用到了定時函數 sleep()
#include <math.h>int main()
{int i;short win_width, win_height; // 定義窗口的寬度和高度win_width = 480;win_height = 360;initgraph(win_width, win_height); // 初始化窗口(黑屏)for (i = 0; i < 256; i += 5){setbkcolor(RGB(i, i, i)); // 設置背景色,原來默認黑色cleardevice(); // 清屏(取決于背景色)Sleep(15); // 延時 15ms}closegraph(); // 關閉繪圖界面return 0;
}
這段代碼運行后,屏幕會逐漸變亮。這是因為背景色不斷刷新為 RGB(i, i, i)。C 語言中的顏色使用十六進制表示,RGB
函數可以將 0~255 范圍內的三個整數三原色轉換為十六進制。cleardevice()
函數用于清屏,通常只在初始化時出現。Sleep()
是毫秒級延遲,界面變亮時間不一定是準確的 15ms×255/5=0.765s,因為其他語句也需要執行時間。closegraph()
用于關閉繪圖界面。如果初始化了繪圖界面但未在主函數結束前關閉它,可能會引發一些莫名其妙的錯誤,因此該函數必不可少。
2.2 創建按鈕
在界面中創建按鈕需要繪制矩形和打印文字。以下是代碼示例:
#include <graphics.h> // 引用圖形庫頭文件
#include <conio.h>
#include <stdio.h>
#include <windows.h> // 用到了定時函數 sleep()
#include <math.h>int r1[] = {30, 20, 130, 60}; // 輸入按鈕的矩形參數
int r2[] = {170, 20, 220, 60}; // 運行按鈕的矩形參數
int r3[] = {260, 20, 310, 60}; // 退出按鈕的矩形參數int main()
{int i;short win_width, win_height; // 定義窗口的寬度和高度win_width = 480;win_height = 360;initgraph(win_width, win_height); // 初始化窗口(黑屏)for (i = 0; i < 256; i += 5){setbkcolor(RGB(i, i, i)); // 設置背景色,原來默認黑色cleardevice(); // 清屏(取決于背景色)Sleep(15); // 延時 15ms}RECT R1 = {r1[0], r1[1], r1[2], r1[3]}; // 矩形指針 R1RECT R2 = {r2[0], r2[1], r2[2], r2[3]}; // 矩形指針 R2RECT R3 = {r3[0], r3[1], r3[2], r3[3]}; // 矩形指針 R3LOGFONT f; // 字體樣式指針gettextstyle(&f); // 獲取字體樣式_tcscpy(f.lfFaceName, _T("宋體")); // 設置字體為宋體f.lfQuality = ANTIALIASED_QUALITY; // 設置輸出效果為抗鋸齒settextstyle(&f); // 設置字體樣式settextcolor(BLACK); // BLACK 在 graphic.h 頭文件中被定義為黑色的顏色常量drawtext("輸入參數", &R1, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R1 內輸入文字,水平居中,垂直居中,單行顯示drawtext("運行", &R2, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R2 內輸入文字,水平居中,垂直居中,單行顯示drawtext("退出", &R3, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R3 內輸入文字,水平居中,垂直居中,單行顯示setlinecolor(BLACK);rectangle(r1[0], r1[1], r1[2], r1[3]);rectangle(r2[0], r2[1], r2[2], r2[3]);rectangle(r3[0], r3[1], r3[2], r3[3]);system("pause"); // 暫停,為了顯示closegraph();return 0;
}
矩形指針 RECT
使用句柄定義,不可中途再次賦值。其格式為 RECT r = {X1, Y1, X2, Y2}
,其中 X1 和 X2 分別是矩形的左邊和右邊的橫坐標,Y1 和 Y2 分別是矩形的上邊和下邊的縱坐標。DT_CENTER | DT_VCENTER | DT_SINGLELINE
是描述填充格式的常量。drawtext
函數用于在矩形區域內書寫文字,無需再計算文字的坐標和設置大小,使用起來非常方便。LOGFONT
是字體樣式指針,通過 gettextstyle
函數獲取當前字體類型,再通過 settextstyle
函數加以設置。這里僅修改了字體名稱和顯示質量,還可以修改斜體、下劃線等屬性,更詳細的內容請參考幫助文檔。
2.3 鼠標操作
2.3.1 單擊特效
鼠標是輸入設備,只要發生以下事件,就會暫存于鼠標消息列表中,操作系統會依次響應列表中的鼠標消息事件。常用的鼠標事件如下:
WM_MOUSEMOVE
:鼠標移動WM_MOUSEWHEEL
:鼠標滾輪滾動WM_LBUTTONDOWN
:鼠標左鍵按下WM_LBUTTONUP
:鼠標左鍵彈起WM_LBUTTONDBLCLK
:鼠標左鍵雙擊WM_RBUTTONDOWN
:鼠標右鍵按下WM_RBUTTONUP
:鼠標右鍵彈起WM_RBUTTONDBLCLK
:鼠標右鍵雙擊WM_MBUTTONDOWN
:鼠標中鍵按下WM_MBUTTONUP
:鼠標中鍵彈起WM_MBUTTONDBLCLK
:鼠標中鍵雙擊
以下是實現鼠標左鍵單擊特效的代碼示例:
#include <graphics.h> // 引用圖形庫頭文件
#include <conio.h>
#include <stdio.h>
#include <windows.h> // 用到了定時函數 sleep()
#include <math.h>int r1[] = {30, 20, 130, 60}; // 輸入按鈕的矩形參數
int r2[] = {170, 20, 220, 60}; // 運行按鈕的矩形參數
int r3[] = {260, 20, 310, 60}; // 退出按鈕的矩形參數int main()
{int i;short win_width, win_height; // 定義窗口的寬度和高度win_width = 480;win_height = 360;initgraph(win_width, win_height); // 初始化窗口(黑屏)for (i = 0; i < 256; i += 5){setbkcolor(RGB(i, i, i)); // 設置背景色,原來默認黑色cleardevice(); // 清屏(取決于背景色)Sleep(15); // 延時 15ms}RECT R1 = {r1[0], r1[1], r1[2], r1[3]}; // 按鈕 1 的矩形區域RECT R2 = {r2[0], r2[1], r2[2], r2[3]}; // 按鈕 2 的矩形區域RECT R3 = {r3[0], r3[1], r3[2], r3[3]}; // 按鈕 3 的矩形區域LOGFONT f;gettextstyle(&f); // 獲取字體樣式_tcscpy(f.lfFaceName, _T("宋體")); // 設置字體為宋體f.lfQuality = ANTIALIASED_QUALITY; // 設置輸出效果為抗鋸齒settextstyle(&f); // 設置字體樣式settextcolor(BLACK); // BLACK 在 graphic.h 頭文件中被定義為黑色的顏色常量drawtext("輸入參數", &R1, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R1 內輸入文字,水平居中,垂直居中,單行顯示drawtext("運行", &R2, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R2 內輸入文字,水平居中,垂直居中,單行顯示drawtext("退出", &R3, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R3 內輸入文字,水平居中,垂直居中,單行顯示setlinecolor(BLACK);rectangle(r1[0], r1[1], r1[2], r1[3]);rectangle(r2[0], r2[1], r2[2], r2[3]);rectangle(r3[0], r3[1], r3[2], r3[3]);MOUSEMSG m; // 鼠標指針setrop2(R2_NOTXORPEN); // 二元光柵——NOT(屏幕顏色 XOR 當前顏色)while (true){m = GetMouseMsg(); // 獲取一條鼠標消息if (m.uMsg == WM_LBUTTONDOWN){for (i = 0; i <= 10; i++){setlinecolor(RGB(25 * i, 25 * i, 25 * i)); // 設置圓顏色circle(m.x, m.y, 2 * i);Sleep(25); // 停頓 25mscircle(m.x, m.y, 2 * i); // 抹去剛剛畫的圓}FlushMouseMsgBuff(); // 清空鼠標消息緩存區}}system("pause"); // 暫停,為了顯示closegraph();return 0;
}
每次點擊鼠標左鍵時,鼠標點擊處會出現一個逐漸擴大并淡出的圓。當循環體內 Sleep
的時間大于 20ms 時,視覺效果會更明顯。每次響應鼠標左鍵單擊事件后,都會調用一次清空鼠標消息緩存區的函數 FlushMouseMsgBuff()
。如果沒有這個函數,快速連續單擊鼠標左鍵多次時,特效會重復播放,即使停止單擊,程序仍會繼續播放單擊特效,因為鼠標消息隊列中的消息尚未處理完畢。
這里需要解釋的是二元光柵設置函數 setrop2()
。二元光柵是混合背景色和當前顏色的模式。我們采用的是同或(NOT XOR)的方式,若底色為白色(1),則當前顏色不變;若底色是黑色(0),則當前顏色反色。采用這種方式的原因是,我們在第二次抹去原來的圓時不能使用白色,否則如果背景色原本為黑色(比如按鈕和文字),也會被抹成白色。而背景色與任意一個顏色同或兩次都為其本身,即可起到還原背景色的效果。這里的背景色與 cleardevice()
前面的背景色不同,它是指執行這一條繪畫指令之前屏幕上的顏色。
2.3.2 光標感應
當鼠標移到按鈕上時,按鈕會有所變化,移開按鈕時又會恢復原樣。這里采用簡單的填充顏色方法,即按鈕變色。需要解決的問題是按鈕變色后按鈕的文字不能被覆蓋,因此仍需使用二元光柵。為了方便起見,將三個按鈕的數組合并為一個二維數組,在鼠標事件中更容易使用和分配任務。以下是代碼示例:
#include <graphics.h> // 引用圖形庫頭文件
#include <conio.h>
#include <stdio.h>
#include <windows.h> // 用到了定時函數 sleep()
#include <math.h>int r[3][4] = {{30, 20, 130, 60}, {170, 20, 220, 60}, {260, 20, 310, 60}}; // 三個按鈕的二維數組int button_judge(int x, int y)
{if (x > r[0][0] && x < r[0][2] && y > r[0][1] && y < r[0][3]) return 1;if (x > r[1][0] && x < r[1][2] && y > r[1][1] && y < r[1][3]) return 2;if (x > r[2][0] && x < r[2][2] && y > r[2][1] && y < r[2][3]) return 3;return 0;
}int main()
{int i, event = 0;short win_width, win_height; // 定義窗口的寬度和高度win_width = 480;win_height = 360;initgraph(win_width, win_height); // 初始化窗口(黑屏)for (i = 0; i < 256; i += 5){setbkcolor(RGB(i, i, i)); // 設置背景色,原來默認黑色cleardevice(); // 清屏(取決于背景色)Sleep(15); // 延時 15ms}RECT R1 = {r[0][0], r[0][1], r[0][2], r[0][3]};RECT R2 = {r[1][0], r[1][1], r[1][2], r[1][3]};RECT R3 = {r[2][0], r[2][1], r[2][2], r[2][3]};LOGFONT f;gettextstyle(&f); // 獲取字體樣式_tcscpy(f.lfFaceName, _T("宋體")); // 設置字體為宋體f.lfQuality = ANTIALIASED_QUALITY; // 設置輸出效果為抗鋸齒settextstyle(&f); // 設置字體樣式settextcolor(BLACK); // BLACK 在 graphic.h 頭文件中被定義為黑色的顏色常量drawtext("輸入參數", &R1, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R1 內輸入文字,水平居中,垂直居中,單行顯示drawtext("運行", &R2, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R2 內輸入文字,水平居中,垂直居中,單行顯示drawtext("退出", &R3, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R3 內輸入文字,水平居中,垂直居中,單行顯示setlinecolor(BLACK);rectangle(r[0][0], r[0][1], r[0][2], r[0][3]);rectangle(r[1][0], r[1][1], r[1][2], r[1][3]);rectangle(r[2][0], r[2][1], r[2][2], r[2][3]);MOUSEMSG m; // 鼠標指針while (true){m = GetMouseMsg(); // 獲取一條鼠標消息switch (m.uMsg){case WM_MOUSEMOVE:setrop2(R2_XORPEN);setlinecolor(LIGHTCYAN); // 線條顏色為亮青色setlinestyle(PS_SOLID, 3); // 設置畫線樣式為實線,寬度為 3setfillcolor(WHITE); // 填充顏色為白色if (button_judge(m.x, m.y) != 0){if (event != button_judge(m.x, m.y)){event = button_judge(m.x, m.y); // 記錄這一次觸發的按鈕fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]); // 有框填充矩形(X1, Y1, X2, Y2)}}else{if (event != 0) // 上次觸發的按鈕未被修正為原來的顏色{fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]); // 兩次同或為原來顏色event = 0;}}break;case WM_LBUTTONDOWN:setrop2(R2_NOTXORPEN); // 二元光柵——NOT(屏幕顏色 XOR 當前顏色)for (i = 0; i <= 10; i++){setlinecolor(RGB(25 * i, 25 * i, 25 * i)); // 設置圓顏色circle(m.x, m.y, 2 * i);Sleep(30); // 停頓 30mscircle(m.x, m.y, 2 * i); // 抹去剛剛畫的圓}break;FlushMouseMsgBuff(); // 清空鼠標消息緩存區}}system("pause"); // 暫停,為了顯示return 0;
}
在鼠標移動事件(case WM_MOUSEMOVE
)中,使用了屏幕顏色與當前顏色異或的方式。fillrectangle
函數用于繪制一個有框填充矩形,其大小與原按鈕一致。由于線條顏色為亮青色,填充顏色為白色(1),白色填充顏色與屏幕顏色異或后,取的是屏幕顏色的反色。按鈕的邊框是黑色(0),它與亮青色異或后,會保留原來的亮青色。與同或一樣,異或兩次等于沒有執行操作,因此可以還原到原屏幕畫布的顏色。
2.3.3 進度條
涉及進度條時,通常會結合一個簡單的程序來展示進度條的變化。這里設計了一個簡單的彈性球軌跡作圖程序。假設球的半徑為 ( R ),初始高度為 ( h_0 ),初速度為 0(自由落體),非彈性碰撞時能量損失率為 ( \alpha )。計算部分子函數如下:
int simulation()
{float dt = 0.01; // 仿真間隔 10mslong int N = (long int)(sim_t / dt); // 迭代次數float *h = (float *)calloc(N, sizeof(float)); // 高度float *v = (float *)calloc(N, sizeof(float)); // 速度(豎直方向)long int i; // 迭代變量for (i = 1; i < N; i++){if (h[i - 1] > R) // 未發生碰撞{v[i] = v[i - 1] - 9.8 * dt; // 速度計算}else // 發生碰撞,動能損失 \( \alpha \),速度損失 \( \sqrt{\alpha} \){v[i] = -sqrt(alpha) * v[i - 1];}}free(h);free(v); // 釋放內存return 0;
}
接下來,需要定義繪圖網格的函數:
void init_figure()
{int i;setrop2(R2_COPYPEN); // 當前顏色setlinecolor(BLACK);setlinestyle(PS_SOLID); // 實線rectangle(30, 100, 420, 330); // 外框線setlinestyle(PS_DOT); // 點線for (i = 30 + 39; i < 420; i += 39){line(i, 100, i, 330); // 豎直輔助線}for (i = 100 + 23; i < 330; i += 23){line(30, i, 420, i); // 水平輔助線}
}
使用 rectangle
函數繪制網格外框架,使用 line
函數依次畫出輔助線。目標是將高度 ( h ) 的坐標轉換到網格上,繪制出球心的軌跡。以下是改進后的 simulation
函數代碼:
int simulation()
{char t[3]; // 百分值的字符char *out_text; // 帶百分號的百分字符float dt = 0.01; // 仿真間隔 10msfloat dy = 230 / h0; // 單位縱坐標long int N = (long int)(sim_t / dt); // 迭代次數float *h = (float *)calloc(N, sizeof(float)); // 高度float *v = (float *)calloc(N, sizeof(float)); // 速度(豎直方向)long int i; // 迭代變量float process_duty; // 進度RECT r = {370, 35, 400, 65}; // 百分值顯示區域的矩形指針init_figure(); // 初始化圖像網格setrop2(R2_COPYPEN); // 當前顏色setfillcolor(WHITE);setlinecolor(WHITE);fillrectangle(354, 19, 411, 81); // 覆蓋原進度條區域setlinestyle(PS_NULL); // 無線條setbkmode(TRANSPARENT); // 設置文字填充背景為透明// 計算步驟h[0] = h0;v[0] = 0;BeginBatchDraw(); // 開始緩存區for (i = 1; i < N; i++){if (h[i - 1] > R) // 未發生碰撞{v[i] = v[i - 1] - 9.8 * dt; // 速度計算}else // 發生碰撞,動能損失 \( \alpha \),速度損失 \( \sqrt{\alpha} \){v[i] = -sqrt(alpha) * v[i - 1];}setfillcolor(WHITE);setlinecolor(WHITE);fillrectangle(354, 19, 416, 81); // 覆蓋原進度條區域h[i] = h[i - 1] + v[i] * dt; // 高度計算process_duty = (i + 1) / (float)(N);setlinestyle(PS_SOLID);putpixel(30 + (int)(process_duty * 390), 330 - (int)(h[i] * dy), RED); // 畫點 putpixel(X, Y, color*)setfillcolor(BLUE);setlinestyle(PS_NULL);fillpie(355, 20, 415, 80, 0, process_duty * 2 * PI); // 繪制環形進度條setfillcolor(WHITE);fillcircle(385, 50, 20); // 覆蓋中心部分sprintf(t, "%d", (int)(process_duty * 100.0)); // 整型轉換為字符串out_text = strcat(t, "%"); // 添加一個百分號drawtext(out_text, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 顯示進度百分比Sleep(dt * 1000); // 延時FlushBatchDraw(); // 刷新緩存區}EndBatchDraw(); // 結束緩存區free(h);free(v);return 0;
}
這里使用了 putpixel
函數繪制球心軌跡,fillpie
函數繪制環形進度條,fillcircle
函數覆蓋中心部分以形成環形效果。FlushBatchDraw
函數用于刷新緩存區,與 BeginBatchDraw
和 EndBatchDraw
一起使用,可以批量繪圖后再刷新畫板。
3. 完整代碼及效果
以下是完整的代碼示例,包含按鈕、鼠標操作、進度條和彈性球軌跡作圖功能:
#include <graphics.h> // 引用圖形庫頭文件
#include <conio.h>
#include <stdio.h>
#include <windows.h> // 用到了定時函數 sleep()
#include <math.h>
#include <string.h>
#define PI 3.1416
int r[3][4] = {{30, 20, 130, 60}, {170, 20, 220, 60}, {260, 20, 310, 60}}; // 三個按鈕的二維數組
float alpha, R, h0, sim_t; // 碰撞時的能量損失率,球的半徑、初始高度、仿真時間// 按鈕判斷函數
int button_judge(int x, int y)
{if (x > r[0][0] && x < r[0][2] && y > r[0][1] && y < r[0][3]) return 1;if (x > r[1][0] && x < r[1][2] && y > r[1][1] && y < r[1][3]) return 2;if (x > r[2][0] && x < r[2][2] && y > r[2][1] && y < r[2][3]) return 3;return 0;
}// 初始化圖像
void init_figure()
{int i;setrop2(R2_COPYPEN); // 當前顏色setlinecolor(BLACK);setlinestyle(PS_SOLID); // 實線rectangle(30, 100, 420, 330); // 外框線setlinestyle(PS_DOT); // 點線for (i = 30 + 39; i < 420; i += 39){line(i, 100, i, 330); // 豎直輔助線}for (i = 100 + 23; i < 330; i += 23){line(30, i, 420, i); // 水平輔助線}
}// 仿真運行
int simulation()
{char t[3]; // 百分值的字符char *out_text;float dt = 0.01; // 仿真間隔 10msfloat dy = 230 / h0; // 單位縱坐標long int N = (long int)(sim_t / dt); // 迭代次數float *h = (float *)calloc(N, sizeof(float)); // 高度float *v = (float *)calloc(N, sizeof(float)); // 速度(豎直方向)long int i; // 迭代變量float process_duty; // 進度RECT r = {370, 35, 400, 65}; // 百分值顯示區域的矩形指針init_figure(); // 初始化圖像網格setrop2(R2_COPYPEN); // 當前顏色setfillcolor(WHITE);setlinecolor(WHITE);fillrectangle(354, 19, 411, 81); // 覆蓋原進度條區域setlinestyle(PS_NULL); // 無線條setbkmode(TRANSPARENT); // 設置文字填充背景為透明// 計算步驟h[0] = h0;v[0] = 0;BeginBatchDraw(); // 開始緩存區for (i = 1; i < N; i++){if (h[i - 1] > R) // 未發生碰撞{v[i] = v[i - 1] - 9.8 * dt; // 速度計算}else // 發生碰撞,動能損失 \( \alpha \),速度損失 \( \sqrt{\alpha} \){v[i] = -sqrt(alpha) * v[i - 1];}setfillcolor(WHITE);setlinecolor(WHITE);fillrectangle(354, 19, 416, 81); // 覆蓋原進度條區域h[i] = h[i - 1] + v[i] * dt; // 高度計算process_duty = (i + 1) / (float)(N);setlinestyle(PS_SOLID);putpixel(30 + (int)(process_duty * 390), 330 - (int)(h[i] * dy), RED); // 畫點 putpixel(X, Y, color*)setfillcolor(BLUE);setlinestyle(PS_NULL);fillpie(355, 20, 415, 80, 0, process_duty * 2 * PI); // 繪制環形進度條setfillcolor(WHITE);fillcircle(385, 50, 20); // 覆蓋中心部分sprintf(t, "%d", (int)(process_duty * 100.0)); // 整型轉換為字符串out_text = strcat(t, "%"); // 添加一個百分號drawtext(out_text, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 顯示進度百分比Sleep(dt * 1000); // 延時FlushBatchDraw(); // 刷新緩存區}EndBatchDraw(); // 結束緩存區free(h);free(v);return 0;
}int main()
{int i, event = 0;char s[30]; // 輸入字符串變量short win_width, win_height; // 定義窗口的寬度和高度win_width = 480;win_height = 360;initgraph(win_width, win_height); // 初始化窗口(黑屏)for (i = 0; i < 256; i += 5){setbkcolor(RGB(i, i, i)); // 設置背景色,原來默認黑色cleardevice(); // 清屏(取決于背景色)Sleep(30); // 延時 30ms}RECT R1 = {r[0][0], r[0][1], r[0][2], r[0][3]};RECT R2 = {r[1][0], r[1][1], r[1][2], r[1][3]};RECT R3 = {r[2][0], r[2][1], r[2][2], r[2][3]};LOGFONT f; // 字體樣式指針gettextstyle(&f); // 獲取字體樣式_tcscpy(f.lfFaceName, _T("宋體")); // 設置字體為宋體f.lfQuality = ANTIALIASED_QUALITY; // 設置輸出效果為抗鋸齒settextstyle(&f); // 設置字體樣式settextcolor(BLACK); // BLACK 在 graphic.h 頭文件中被定義為黑色的顏色常量drawtext("輸入參數", &R1, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R1 內輸入文字,水平居中,垂直居中,單行顯示drawtext("運行", &R2, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R2 內輸入文字,水平居中,垂直居中,單行顯示drawtext("退出", &R3, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 在矩形區域 R3 內輸入文字,水平居中,垂直居中,單行顯示setlinecolor(BLACK);rectangle(r[0][0], r[0][1], r[0][2], r[0][3]);rectangle(r[1][0], r[1][1], r[1][2], r[1][3]);rectangle(r[2][0], r[2][1], r[2][2], r[2][3]);MOUSEMSG m; // 鼠標指針while (true){m = GetMouseMsg(); // 獲取一條鼠標消息switch (m.uMsg){case WM_MOUSEMOVE:setrop2(R2_XORPEN);setlinecolor(LIGHTCYAN); // 線條顏色為亮青色setlinestyle(PS_SOLID, 3); // 設置畫線樣式為實線,寬度為 3setfillcolor(WHITE); // 填充顏色為白色if (button_judge(m.x, m.y) != 0){if (event != button_judge(m.x, m.y)){event = button_judge(m.x, m.y); // 記錄這一次觸發的按鈕fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]); // 有框填充矩形(X1, Y1, X2, Y2)}}else{if (event != 0) // 上次觸發的按鈕未被修正為原來的顏色{fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]); // 兩次同或為原來顏色event = 0;}}break;case WM_LBUTTONDOWN:setrop2(R2_NOTXORPEN); // 二元光柵——NOT(屏幕顏色 XOR 當前顏色)for (i = 0; i <= 10; i++){setlinecolor(RGB(25 * i, 25 * i, 25 * i)); // 設置圓顏色circle(m.x, m.y, 2 * i);Sleep(20); // 停頓 20mscircle(m.x, m.y, 2 * i); // 抹去剛剛畫的圓}// 按照按鈕判斷左鍵單擊后的操作switch (button_judge(m.x, m.y)){case 1:InputBox(s, 30, "請輸入碰撞時的能量損失率、球的半徑、初始高度、仿真時間");sscanf(s, "%f %f %f %f", &alpha, &R, &h0, &sim_t); // 將輸入字符串依次掃描到全局變量中FlushMouseMsgBuffer(); // 單擊事件后清空鼠標消息break;case 2:simulation(); // 仿真運行FlushMouseMsgBuffer(); // 單擊事件后清空鼠標消息break;case 3:closegraph(); // 關閉繪圖環境exit(0); // 正常退出default:FlushMouseMsgBuffer(); // 單擊事件后清空鼠標消息break;}break;}}return 0;
}
希望本文對您有所幫助,謝謝閱讀。
C 語言實現動畫控制
非線性光學元件于 2018-12-22 20:47:28 發布
原材料
下載 EasyX 2014 冬至版,將 lib
文件放在編譯器默認的 lib
文件夾中,h
頭文件放在編譯器默認的 include
文件夾中即可。下載鏈接:EasyX Graphics Library for C++
說明
C 語言可以使用系統內部的定時函數 sleep
和 usleep
進行定時(需要 windows.h
頭文件),但繪圖窗口需要額外的圖形庫支持。EasyX 提供了繪圖功能,可以為您的 C 編譯器帶來革命性的變化。
一場革命
EasyX 的壓縮包中包含一個幫助文檔,雖然內容豐富但不太方便查閱。希望正在使用 EasyX 的開發者能多分享一些資源。以下是一個沿著指定半徑依次繪制 12 個不同顏色的圓并依次擦除的小動畫程序。代碼中對不太容易理解的部分加了注釋,通過圖形畫法學習 C 語言語法,既生動又簡單,可以快速跨越語法障礙。
#include <graphics.h> // 引用圖形庫頭文件
#include <conio.h>
#include <stdio.h>
#include <windows.h> // 用到了定時函數 sleep()
#include <math.h>
#define PI 3.14159265 // 畫圓必備
int a[] = {0, 0xAA0000, 0x00AA00, 0xAAAA00, 0x0000AA, 0xAA00AA, 0x0055AA, 0xAAAAAA, 0x555555, 0xFF5555, 0x55FF55, 0xFFFF55, 0x5555FF, 0xFF55FF, 0x55FFFF, 0xFFFFFF}; // a[] 是顏色數組
// a 數組存放的顏色依次為
/* |0:黑色 |1:藍色 |2:綠色 |3:青色 |4:紅色|5:紫色 |6:棕色 |7:淺灰 |8:深灰 |9:亮藍|10:亮綠 |11:亮青 |12:亮紅 |13:亮紫 |14:黃色 |15:白色
*/
int main()
{system("color 0B"); // 設置字體為亮藍色,純粹為了好看short x, y; // 圓心坐標int R; // 旋轉半徑int color[6] = {1, 2, 3, 4, 5, 6}; // 指定圓的顏色int i = 0;char t;printf("C 語言繪圖實驗:\n");printf("請選擇畫布大小(以空格分隔):\n");scanf("%d %d", &x, &y);initgraph(x, y, SHOWCONSOLE); // 創建繪圖窗口,大小為 640x480 像素printf("請輸入旋轉半徑: ");scanf("%d", &R);printf("請選擇 6 種圓的顏色:\n");printf("|0:黑色\n|1:藍色\t|2:綠色\t|3:青色\t|4:紅色\t|5:紫色\n|6:棕色\t|7:淺灰\t|8:深灰\t|9:亮藍\t|10:亮綠\n|11:亮青\t|12:亮紅\t|13:亮紫\t|14:黃色\t|15:白色\n");scanf("%d %d %d %d %d %d", &color[0], &color[1], &color[2], &color[3], &color[4], &color[5]); // 錄入 6 種不同的顏色printf("\r按任意鍵繼續:\n");while (_getch()) // _getch() 是按下任意鍵即返回非零值的函數,與 getchar() 不同,不經過標準輸入流的緩存區{for (i = 0; i < 12; i++){setlinecolor(RGB(0, 0, 0)); // 設置當前線條顏色setfillcolor(a[color[i % 6]]); // 設置當前填充顏色fillcircle(x / 2 + R * cos(i * PI / 6), y / 2 + R * sin(i * PI / 6), R * (PI / 12) * 0.9); // 繪制填充圓Sleep(300); // 延時 300ms}for (i = 0; i < 12; i++){setlinecolor(RGB(0, 0, 0)); // 設置當前線條顏色setfillcolor(a[0]); // 背景色(黑色)覆蓋掉原來的圖形fillcircle(x / 2 + R * cos(i * PI / 6), y / 2 + R * sin(i * PI / 6), R * (PI / 12) * 0.9);Sleep(300); // 延時 300ms}}return 0;
}
以下是程序運行主界面:
以下是繪圖界面:
C 語言寫字符動畫
Z.IA 已于 2024-12-01 14:26:02 修改
用 C 在控制臺寫了一個動畫,代碼沒做優化,十分簡單粗暴。
新版本 win 系統需使用老版本 cmd。
以管理員身份運行 exe 文件即可。
#include <stdio.h>#include <windows.h>void color (int x){SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),x); }int main()
{int i,j,q=28,xxl=22,h=27,m=33,cc=2,xxx=1;char s[68][53];char z[140][53];char zx[148][100];for(i=0;i<35;i++){for(j=0;j<28;j++)zx[i][j]=' ';for(j=28;j<=35;j++)zx[i][j]='O';for(j=36;j<100;j++)zx[i][j]=' '; } for(i=35;i<134;i++){for(j=0;j<h;j++)zx[i][j]=' ';for(j=h;j<h+4;j++)zx[i][j]='O';for(j=h+4;j<=m;j++)zx[i][j]=' ';for(j=m;j<m+4;j++)zx[i][j]='O';for(j=m+4;j<100;j++)zx[i][j]=' ';if(i<57){h--;m++;}if(i>110){h++;m--;}}for(i=134;i<148;i++){for(j=0;j<28;j++)zx[i][j]=' ';for(j=28;j<=35;j++)zx[i][j]='O';for(j=36;j<100;j++)zx[i][j]=' '; } for(i=48;i<=51;i++)for(j=31;j<=35;j++)zx[i][j]='*';for(i=129;i<=133;i++){for(j=15;j<=17;j++)zx[i][j]='*';for(j=46;j<=48;j++)zx[i][j]='*';}for(i=0;i<40;i++) //直行 {for(j=0;j<28;j++)z[i][j]=' ';for(j=28;j<36;j++)z[i][j]='O';for(j=36;j<53;j++)z[i][j]=' ';}for(i=40;i<61;i++) //拐彎+回; {for(j=0;j<q;j++)z[i][j]=' ';for(j=q;j<q+8;j++)z[i][j]='O';for(j=q+8;j<60;j++)z[i][j]=' ';if(i<53) //右移長度控制 q-=2; if(i>53&&i<60) //回長度控制 q++;}for(i=61;i<101;i++) //直行 {for(j=0;j<8;j++)z[i][j]=' ';for(j=8;j<16;j++)z[i][j]='O';for(j=16;j<53;j++)z[i][j]=' ';}for(i=101;i<124;i++) //拐彎+回; {for(j=0;j<q;j++)z[i][j]=' ';for(j=q;j<q+8;j++)z[i][j]='O';for(j=q+8;j<60;j++)z[i][j]=' ';if(i<114) //右移長度控制 q+=2; if(i>114&&i<121) //回長度控制 q--;}for(i=124;i<140;i++){for(j=0;j<28;j++)z[i][j]=' ';for(j=28;j<=35;j++)z[i][j]='O';for(j=36;j<100;j++)z[i][j]=' '; } for(i=54;i<=57;i++)z[i][30]=z[i][31]=z[i][32]='*';for(i=116;i<=119;i++)z[i][14]=z[i][15]=z[i][16]='*';for(i=0;i<40;i++) //直行 {for(j=0;j<22;j++)s[i][j]=' ';for(j=22;j<29;j++)s[i][j]='O';for(j=29;j<53;j++)s[i][j]=' ';}for(i=40;i<68;i++) //拐彎+回; {for(j=0;j<xxl;j++)s[i][j]=' ';for(j=xxl;j<xxl+8;j++)s[i][j]='O';for(j=xxl+8;j<53;j++)s[i][j]=' ';if(i<53) //右移長度控制 xxl++; if(i>53&&i<60) //回長度控制 xxl--;}int k,c=28,begin=28,end=39;
q=39;char x[104][40]; for(i=0;i<=10;i++){for(j=0;j<q;j++)x[i][j]=' ';for(j=q;j<=39;j++)x[i][j]='O';if(q>27)q--;}for(i=90;i<=101;i++){for(j=0;j<=c;j++)x[i][j]=' ';for(j=c+1;j<=39;j++)x[i][j]='O';if(c<39)c++;}for (i=11;i<=50;i++){for (j=0;j<begin;j++)x[i][j]=' ';for (j=begin;j<=end;j++)x[i][j]='O';for (j=end+1;j<=39;j++)x[i][j]=' ';if(begin>0)begin--;if(end>0)end--;}end++;for (i=51;i<=89;i++){for (j=0;j<begin;j++)x[i][j]=' ';for (j=begin;j<=end;j++)x[i][j]='O';for (j=end+1;j<=39;j++)x[i][j]=' ';if(end>=11)begin++;if(end<39)end++;}//扭動組賦值 char kd[100][40];
for(i=0;i<100;i++)
{for(j=0;j<38;j++)kd[i][j]=' ';kd[i][39]='O';
}int p=1,g=1,xs=3;char wy[200][120];for(i=0;i<200;i++)for(j=0;j<120;j++)wy[i][j]=' ';for(i=0;i<40;i++){for(j=0;j<8;j++)wy[i][j]=' ';for(j=8;j<16;j++)wy[i][j]='O';for(j=16;j<100;j++)wy[i][j]=' ';}/*直行賦值*/ j=16;for(i=40;i<76;i++){wy[i][j]='O'; if((i-39)%4==0&&i>=43)g+=p;j+=g;if(i==55)p=-p;} j=15;p=1;g=1;for(i=42;i<78;i++){wy[i][j]='O'; if((i-41)%4==0&&i>=45)g+=p;j+=g;if(i==57)p=-p;} j=14;p=1;g=1;for(i=44;i<80;i++){wy[i][j]='O'; if((i-43)%4==0&&i>=47)g+=p;j+=g;if(i==59)p=-p;} j=13;p=1;g=1;for(i=46;i<82;i++){wy[i][j]='O'; if((i-45)%4==0&&i>=49)g+=p;j+=g;if(i==61)p=-p;} j=12;p=1;g=1;for(i=48;i<84;i++){wy[i][j]='O'; if((i-47)%4==0&&i>=51)g+=p;j+=g;if(i==63)p=-p;} // 第5組; j=11;p=1;g=1;for(i=50;i<86;i++){wy[i][j]='O'; if((i-49)%4==0&&i>=53)g+=p;j+=g;if(i==65)p=-p;} j=10;p=1;g=1;for(i=52;i<88;i++){wy[i][j]='O'; if((i-51)%4==0&&i>=55)g+=p;j+=g;if(i==67)p=-p;} j=9;p=1;g=1;for(i=54;i<90;i++){wy[i][j]='O'; if((i-53)%4==0&&i>=57)g+=p;j+=g;if(i==69)p=-p;} p=54;for(j=8;j<16;j++){for(i=40;i<p;i++)wy[i][j]='O';p-=2;}//補充 O p=75;for(j=116;j>=110;j--){for(i=89;i>p;i--)wy[i][j]='O';p+=2;}for(i=90;i<130;i++){for(j=109;j<117;j++)wy[i][j]='O';}j=108;
p=-1;
g=-1;for(i=130;i<166;i++){wy[i][j]='O'; if((i-129)%4==0&&i>=133)g+=p;j+=g;if(i==145)p=-p;} j=109;
p=-1;
g=-1;for(i=132;i<168;i++){wy[i][j]='O'; if((i-131)%4==0&&i>=135)g+=p;j+=g;if(i==147)p=-p;} j=110;
p=-1;
g=-1;for(i=134;i<170;i++){wy[i][j]='O'; if((i-133)%4==0&&i>=137)g+=p;j+=g;if(i==149)p=-p;} j=111;
p=-1;
g=-1;for(i=136;i<172;i++){wy[i][j]='O'; if((i-135)%4==0&&i>=139)g+=p;j+=g;if(i==151)p=-p;} j=112;
p=-1;
g=-1;for(i=138;i<174;i++){wy[i][j]='O'; if((i-137)%4==0&&i>=141)g+=p;j+=g;if(i==153)p=-p;} j=113;p=-1;g=-1;for(i=140;i<176;i++){wy[i][j]='O'; if((i-139)%4==0&&i>=143)g+=p;j+=g;if(i==155)p=-p;} j=114;p=-1;g=-1;for(i=142;i<178;i++){wy[i][j]='O'; if((i-141)%4==0&&i>=145)g+=p;j+=g;if(i==157)p=-p;} j=115;p=-1;g=-1;for(i=144;i<180;i++){wy[i][j]='O'; if((i-143)%4==0&&i>=147)g+=p;j+=g;if(i==159)p=-p;} p=165;for(j=8;j<16;j++){for(i=179;i>p;i--)wy[i][j]='O';p+=2;}p=144;for(j=116;j>=110;j--){for(i=130;i<p;i++)wy[i][j]='O';p-=2;}for(i=180;i<200;i++){for(j=0;j<8;j++)wy[i][j]=' ';for(j=8;j<16;j++)wy[i][j]='O';for(j=16;j<100;j++)wy[i][j]=' ';}//菱形部分 char a[50][54];k=15;begin=24;end=27;for(i=0;i<=49;i++)for(j=24;j<=27;j++)a[i][j]='O';for(i=0;i<=24;i++){for(j=0;j<begin;j++)a[i][j]=' ';for(j=begin;j<24;j++)a[i][j]='i';for(j=28;j<=end;j++)a[i][j]='i';for(j=end+1;j<=51;j++)a[i][j]=' ';begin-=1;end+=1;}begin+=1;end-=1;for(i=25;i<=49;i++){for(j=0;j<begin;j++)a[i][j]=' ';for(j=begin;j<24;j++)a[i][j]='i';for(j=28;j<=end;j++)a[i][j]='i';for(j=end+1;j<=51;j++)a[i][j]=' ';begin+=1;end-=1;}system("color f3");for(i=0;i<100;i++){for(j=0;j<40;j++)printf("%c",kd[i][j]);printf("\n\n");Sleep(2);} int yans=2;system("color f3");while(yans--)
{for(i=0;i<=100;i++){for (j=0;j<=39;j++)printf("%c",x[i][j]);if(i<=100)printf("\n\n");Sleep(1);}
}k=3;while (k--){for(i=0;i<=49;i++){for(j=0;j<24;j++){color(3);printf("%c",a[i][j]);}for(j=24;j<=27;j++){color(7);printf("%c",a[i][j]);}for(j=28;j<=51;j++){color(3);printf("%c",a[i][j]);}printf("\n\n");Sleep(1);}}color(7);for(i=47;i<=50;i++)s[i][8]=s[i][9]=s[i][10]='*';for(i=0;i<47;i++){ for(j=0;j<53;j++)printf("%c",s[i][j]);printf("\n\n");Sleep(1);
}for(i=47;i<=50;i++){for(j=0;j<=10;j++){color(4);printf("%c",s[i][j]);}for(j=11;j<53;j++){color(7);printf("%c",s[i][j]);}printf("\n\n");}for(i=51;i<68;i++){for(j=0;j<53;j++)printf("%c",s[i][j]);printf("\n\n");Sleep(1);
}while(cc--)
{for(i=0;i<54;i++)
{ for(j=0;j<53;j++)printf("%c",z[i][j]);printf("\n\n");Sleep(1);
}for(i=54;i<=57;i++){for(j=0;j<=29;j++){color(7);printf("%c",z[i][j]);}for(j=30;j<33;j++){color(4);printf("%c",z[i][j]);}printf("\n\n"); Sleep(1);}color(7); for(i=58;i<116;i++){ for(j=0;j<53;j++)printf("%c",z[i][j]);printf("\n\n");Sleep(1);}for(i=116;i<=119;i++){for(j=0;j<=16;j++){color(4);printf("%c",z[i][j]);}for(j=17;j<53;j++){color(7);printf("%c",z[i][j]);}printf("\n\n"); Sleep(1);}for(i=120;i<140;i++){for(j=0;j<53;j++)printf("%c",z[i][j]);printf("\n\n");Sleep(1);}
}while(xxx--)
{for(i=0;i<48;i++){for(j=0;j<100;j++)printf("%c",zx[i][j]);printf("\n\n");Sleep(1);}for(i=48;i<52;i++){for(j=0;j<31;j++)printf("%c",zx[i][j]);color(4);for(j=31;j<=35;j++)printf("%c",zx[i][j]);color(7);for(j=36;j<100;j++)printf("%c",zx[i][j]);printf("\n\n");Sleep(1);}for(i=52;i<129;i++){for(j=0;j<100;j++)printf("%c",zx[i][j]);printf("\n\n");Sleep(1);}for(i=129;i<134;i++){color(4);for(j=0;j<=17;j++)printf("%c",zx[i][j]);color(7);for(j=18;j<46;j++)printf("%c",zx[i][j]);color(4);for(j=46;j<100;j++)printf("%c",zx[i][j]);printf("\n\n");Sleep(1);}color(7);for(i=134;i<148;i++){for(j=0;j<100;j++)printf("%c",zx[i][j]);printf("\n\n");Sleep(1);}}for(i=0;i<54;i++)
{ for(j=0;j<53;j++)printf("%c",z[i][j]);printf("\n\n");Sleep(1);
}for(i=54;i<=57;i++){for(j=0;j<=29;j++){color(7);printf("%c",z[i][j]);}for(j=30;j<33;j++){color(4);printf("%c",z[i][j]);}printf("\n\n"); Sleep(1);}color(7); for(i=58;i<102;i++){ for(j=0;j<53;j++)printf("%c",z[i][j]);printf("\n\n");Sleep(1);}while(xs--)
{for(i=0;i<200;i++){for(j=0;j<120;j++){color(xs+2);printf("%c",wy[i][j]);}printf("\n\n");Sleep(2);}
}
}
via:
-
C語言圖形化界面——含圖形、按鈕、鼠標、進度條等部件制作(帶詳細代碼、講解及注釋)-CSDN博客
https://blog.csdn.net/weixin_44044411/article/details/104276757 -
C 語言繪圖實驗-CSDN博客
https://blog.csdn.net/weixin_44044411/article/details/85217818 -
C 語言寫字符動畫_c語言字符動畫-CSDN博客
https://blog.csdn.net/qq_60682749/article/details/124206299 -
關于C語言實現easyX一幀一幀播放動態圖(超詳細)_如何用easyx實現簡單的動畫-CSDN博客
https://blog.csdn.net/loneth/article/details/126788379 -
C語言圖形編程(easyX簡明教程) - C語言網
https://www.dotcpp.com/course/easyx/ -
動畫原理 淺析 - Jenaral - 博客園
https://www.cnblogs.com/Jenaral/p/5681815.html -
控制臺動態繪制字符藝術-CSDN博客
https://blog.csdn.net/dandelionLYY/article/details/86744041