GDI绘制时钟效果,与系统时间保持同步,基于Winform
2019獨角獸企業重金招聘Python工程師標準>>>
這是直接在Winform的基礎上進行繪制的。接下來,我對時鐘進行了封裝,封裝成一個名為CSharpQuartz的類,效果如下:
這是把時鐘封裝后,實現的一種效果,CSharpQuartz內部開辟了一個線程,與系統時間,保持同步,每秒刷新一次。所采用的技術也就是GDI和多線程及事件委托。把時鐘封裝成對象后,還為其添加了OnChanged事件,用于對象提供外部
處理之用。接下來就簡單的說下,做次小程序的一些準備工作吧。
? ? ? ? ? ?這也是最近偶爾聽到有朋友問怎樣做時鐘的事,想來,其實也簡單的,只是需要一些耐心和細心,這里主要還利用一些三角函數進行計算。上圖看似很簡單,其實也有很多小細節需要注意。我就把大致繪制的過程簡單說下:
? ? ? ? ? 首先,我們需要定義一個圓,來作為時鐘的輪廓,這里是通過設置時鐘的直徑及winform的寬高,來計算出時鐘在窗體居中的位置。繪制圓的代碼就更簡單了
float w = 300f, h = 300f; float x = (this.Width - w) / 2; float y = (this.Height - h) / 2;float d = w;//直徑 float r = d / 2;//半徑graphics.DrawEllipse(pen, new RectangleF(x, y, w, h));//繪制圓接下來,我們需要計算圓的一周遍布的12個時間點。然后把這些時間點和圓心連在一起,就形成了上圖我們看到的不同時間點的線段。圓心的查找非常簡單,圓心的坐標點,其實就是x軸+半徑r,y軸+半徑r:
PointF pointEclipse = new PointF(x + r, y + r);開始分表繪制12個點與圓心的連線,我這里是以9點為起點,此時,腦海中呈現這樣的畫面
時針一圈12格,每格也就是 Math.PI/6
比如我們計算10點在圓上的坐標P10.
10點所在的點,與x軸的夾角呈30度。那么10點在x上的坐標應該是,x1=x+r-r*Cos(30),以此類推,不難求出12個點的位置,具體代碼如下:
/// <summary> /// <![CDATA[畫時刻線 這里是以9點這個時間坐標為起點 進行360度]]> /// </summary> /// <param name="graphics"><![CDATA[畫布]]></param> /// <param name="x"><![CDATA[圓x坐標]]></param> /// <param name="y"><![CDATA[圓y坐標]]></param> /// <param name="r"><![CDATA[圓x坐標]]></param> private void DrawQuartzLine(Graphics graphics, float x, float y, float r) {//圓心PointF pointEclipse = new PointF(x + r, y + r);float labelX, labelY;//文本坐標float angle = Convert.ToSingle(Math.PI / 6);//角度 30度Font font = new Font(FontFamily.GenericSerif, 12);float _x, _y;//圓上的坐標點using (Brush brush = new System.Drawing.SolidBrush(Color.Red)){using (Pen pen = new Pen(Color.Black, 0.6f)){//一天12H,將圓分為12份 for (int i = 0; i <= 11; i++){PointF p10;//圓周上的點float pAngle = angle * i;float x1, y1;//三、四象限if (pAngle > Math.PI){if ((pAngle - Math.PI) > Math.PI / 2)//鈍角大于90度 {//第三象限x1 = Convert.ToSingle(r * Math.Cos(Math.PI * 2 - pAngle));y1 = Convert.ToSingle(r * Math.Sin(Math.PI * 2 - pAngle));_x = x + r - x1;_y = y + r + y1;labelX = _x - 8;labelY = _y;}else{//第四象限x1 = Convert.ToSingle(r * Math.Cos(pAngle - Math.PI));y1 = Convert.ToSingle(r * Math.Sin(pAngle - Math.PI));_x = x + r + x1;_y = y + r + y1;labelX = _x;labelY = _y;}}//一、二象限else if (pAngle > Math.PI / 2)//鈍角大于90度{//第一象限x1 = Convert.ToSingle(r * Math.Cos(Math.PI - pAngle));y1 = Convert.ToSingle(r * Math.Sin(Math.PI - pAngle));_x = x + r + x1;_y = y + r - y1;labelX = _x;labelY = _y - 20;}else{//第二象限x1 = Convert.ToSingle(r * Math.Cos(pAngle));y1 = Convert.ToSingle(r * Math.Sin(pAngle));_x = x + r - x1;_y = y + r - y1;labelX = _x - 15;labelY = _y - 20;}//上半圓 分成12份,每份 30度if (i + 9 > 12){graphics.DrawString((i + 9 - 12).ToString(), font, brush, labelX, labelY);}else{if (i + 9 == 9){labelX = x - 13;labelY = y + r - 6;}graphics.DrawString((i + 9).ToString(), font, brush, labelX, labelY);}p10 = new PointF(_x, _y);graphics.DrawLine(pen, pointEclipse, p10);}}} }為了輔助計算,我又添加了x軸與y軸的線,就是我們在效果圖中看到的垂直于水平兩條線段。
/// <summary> /// <![CDATA[繪制象限]]> /// </summary> /// <param name="graphics"><![CDATA[畫布]]></param> /// <param name="x"><![CDATA[圓x坐標]]></param> /// <param name="y"><![CDATA[圓y坐標]]></param> /// <param name="r"><![CDATA[圓半徑]]></param> private void DrawQuadrant(Graphics graphics, float x, float y, float r) {float w = r * 2;float extend = 100f;using (Pen pen = new Pen(Color.Black, 1)){#region 繪制象限PointF point1 = new PointF(x - extend, y + r);//PointF point2 = new PointF(x + w + extend, y + r);PointF point3 = new PointF(x + r, y - extend);//PointF point4 = new PointF(x + r, y + w + extend);graphics.DrawLine(pen, point1, point2);graphics.DrawLine(pen, point3, point4);#endregion 繪制象限}}接下來,該繪制指針(時、分、秒),就是我們效果圖中看到的,紅綠藍,三條長短不一的線段,秒針最長,這是和本地系統時間同步,所以要根據當前時間,計算出指針所在的位置。
/// <summary> /// <![CDATA[繪制時、分、秒針]]> /// </summary> /// <param name="graphics"><![CDATA[畫布]]></param> /// <param name="x"><![CDATA[圓x坐標]]></param> /// <param name="y"><![CDATA[圓y坐標]]></param> /// <param name="r"><![CDATA[圓半徑]]></param> private void DrawQuartzShot(Graphics graphics, float x, float y, float r) {if (this.IsHandleCreated){this.Invoke(new Action(() =>{//當前時間DateTime dtNow = DateTime.Now;int h = dtNow.Hour;int m = dtNow.Minute;int s = dtNow.Second;float ha = Convert.ToSingle(Math.PI * 2 / 12);//每小時所弧度 360/12格=30float hm = Convert.ToSingle(Math.PI * 2 / 60);float hs = Convert.ToSingle(Math.PI * 2 / 60);float x1, y1, offset = 60f;using (Pen pen = new Pen(Color.Green, 4)){//時針h = h >= 12 ? h - 12 : h;double angle = h * ha;//當前時針所占弧度x1 = x + r + Convert.ToSingle(Math.Sin(angle) * (r - offset));//通過調整offset的大小,可以控制時針的長短y1 = y + r - Convert.ToSingle(Math.Cos(angle) * (r - offset));//圓心PointF pointEclipse = new PointF(x + r, y + r);PointF pointEnd = new PointF(x1, y1);graphics.DrawLine(pen, pointEclipse, pointEnd);//畫45度角//分針using (Pen penYellow = new Pen(Color.Red, 2)){offset = 30;//分double angelMinutes = hm * m;//每分鐘弧度x1 = x + r + Convert.ToSingle(Math.Sin(angelMinutes) * (r - offset));//通過調整offset的大小,可以控制時針的長短y1 = y + r - Convert.ToSingle(Math.Cos(angelMinutes) * (r - offset));graphics.DrawLine(penYellow, pointEclipse, new PointF(x1, y1));//畫45度角}//秒針using (Pen penYellow = new Pen(Color.Blue, 2)){offset = 20;//分double angelSeconds = hs * s;//每秒鐘弧度x1 = x + r + Convert.ToSingle(Math.Sin(angelSeconds) * (r - offset));//通過調整offset的大小,可以控制時針的長短y1 = y + r - Convert.ToSingle(Math.Cos(angelSeconds) * (r - offset));graphics.DrawLine(penYellow, pointEclipse, new PointF(x1, y1));//畫45度角}}this.lblTime.Text = string.Format("當前時間:{0}:{1}:{2}", h, m, s);}));} }最后,開辟一個線程,來同步更新時針的狀態。
/// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Quartz_Load(object sender, EventArgs e) {timer = new Thread(() =>{if (_graphics == null){_graphics = this.CreateGraphics();_graphics.SmoothingMode = SmoothingMode.HighQuality; //高質量_graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; //高像素偏移質量}while (true){_graphics.Clear(this.BackColor);DrawCaller(_graphics);System.Threading.Thread.Sleep(1000);}});timer.IsBackground = true; }每秒鐘,更新一次,其實就是重繪。
? ? ? 完成了以上幾個步驟,我們就完成GDI繪制時鐘的工作,后來,把它封裝成一個名為CSharpQuartz的對象,具體代碼已經托管到碼云上,點擊此處
? ? ?這就是我們開篇第一張效果圖,帶有Quartz字樣的,至此,關于GDI繪制時鐘與系統時間同步的小程序就這樣完成。時間倉促,某些計算方法買來得及仔細推敲,不足之處,大家多提意見。
?
轉載于:https://my.oschina.net/lichaoqiang/blog/1612040
總結
以上是生活随笔為你收集整理的GDI绘制时钟效果,与系统时间保持同步,基于Winform的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国量子通信再获突破,潘建伟团队完成人类
- 下一篇: 按钮框