• WinForm 画个时钟


    今天学习GDI+,试着想写一个模拟时钟的小程序,原以为很简单实现;但其实还有些复杂,特别是利用三角函数的那部分,让我四处找资料恶补了一下高中数学才算弄清楚,现在就回顾一下这个程序吧.

    程序的目的是要模拟出时钟的效果,那首先就是要画出这个时钟的样子。不考虑美观,一个时钟最简单的组成是一个圆形的表盘,三根直线代表的时针、分针和秒针。

     

    看起来很简单吧,但要怎么样画呢?让我们一步一步来吧:

    1.画表盘

    Graphics g = this.CreateGraphics(); //创建一个Graphics对象

    Pen myPen = new Pen(Color.Blue,1); //创建一个自定义画笔对象

    //创建一个正方形rect
    int myRect_X = this.ClientRectangle.Right/4;
    int myRect_Y = this.ClientRectangle.Bottom/4;
    int myRect_Width;
    int myRect_Height;
    if(this.ClientRectangle.Height< this.ClientRectangle.Width)
    {

    myRect_Width = this.ClientRectangle.Height/2;
    myRect_Height = this.ClientRectangle.Height/2;
    }
    else
    {
    myRect_Width = this.ClientRectangle.Width/2;
    myRect_Height = this.ClientRectangle.Width/2;
    }


    Rectangle rect = new Rectangle(myRect_X,myRect_Y,myRect_Width,myRect_Height);

    g.DrawEllipse(myPen,rect); //画出一个内切于矩形rect的圆

     


    Graphics.DrawEllipse方法有几种重载方式,其中较常用的就是Graphics.DrawEllipse(Pen,Rectangle),这个重载方法需要提供一个Rectangle参数,然后根据这个矩形的形状内切出椭圆,若这个矩形是正方形,则切出来的是一个正圆.

    这段程序最关键的地方就在于得到这个矩形了,因为它间接决定了表盘的位置与大小,Rectangle的构造函数需要四个参数,矩形左上角的X、Y坐标,宽度与高度.

    这里的ClientRectangle所代表的是控件的工作区,它也是一个Rectangle类型的对象,是指控件的边界减去非工作区元素(如滚动条、边框、标题栏和菜单)。相应的Right,Bottom,Width,Height分别代表了右边缘的X坐标,下边缘的Y坐标,宽度与高度.

    为了使这个表盘适应窗口的变化,这里将这些值全部设置为相对与ClientRectangle相关属性的值,如下图:

     

    这样当窗口大小变化时,这个圆的外切矩形也随之改变,假如这个窗口的宽度比高度大太多,或反之,则可能会出现表盘的部分超出工作区域无法显示,为了防止这种情况,加入了条件判断语句。如果宽度大于高度,则圆的直径取高度值;反之则取宽度值。当然高度等于宽度时,圆是在工作区域正中间。

    2.画表针

    将表盘画好后,就可以开始画表针了,这也是最复杂的一步

    //得到中心点坐标
    int centerPoint_X = rect.Right - rect.Width/2;
    int centerPoint_Y = rect.Bottom - rect.Height/2;
    Point centerPoint = new Point(centerPoint_X,centerPoint_Y);

    int s_Len = rect.Width*0.45; //秒针长度
    int m_Len = rect.Width*0.35//分针长度
    int l_Len = rect.Width*0.25 //时针长度

    //得到当前时间
    int h = DateTime.Now.Hour;
    int m = DateTime.Now.Minute;
    int s = DateTime.Now.Second;

    //得到秒针顶点坐标
    int sec_X = (int)(centerPoint.X + Math.Sin(6*Math.PI/180*s)*s_Len);
    int sec_Y = (int)(centerPoint.Y - Math.Cos(6*Math.PI/180*s)*s_Len);
    Point secPoint = new Point(sec_X,sec_Y);

    //得到分针顶点坐标
    int min_X = (int)(centerPoint.X + Math.Sin(6*Math.PI/180*m)*m_Len);
    int min_Y = (int)(centerPoint.Y - Math.Cos(6*Math.PI/180*m)*m_Len);
    Point minPoint = new Point(min_X,min_Y);

    //得到时针顶点坐标
    int hour_X = (int)(centerPoint.X + Math.Sin(h*30+m/2)*Math.PI/180*h_Len);
    int hour_Y = (int)(centerPoint.Y - Math.Cos(h*30+m/2)*Math.PI/180*h_Len);
    Point hourPoint = new Point(hour_X,hour_Y);

    //以不同的颜色连接
    myPen = new Pen(Color.Blue,1);
    g.DrawLine(myPen,centerPoint,secPoint); //连接原点和秒针顶点
    myPen = new Pen(Color.Green,2);
    g.DrawLine(myPen,centerPoint,minPoint); //连接原点和分针顶点
    myPen = new Pen(Color.Red,3);
    g.DrawLine(myPen,centerPoint,hourPoint); //连接原点和时针顶点
    g.Dispose(); //释放Graphics对象使用的所有资源

     

    要画出一条直线,必须要得到这个直线的两个顶点坐标,在这个程序里,三根表针有一个共同的原点(表盘中心点),然后如何得到另外一个点就是我们接下来要做的工作了

     

    我们先来回顾一下三角函数的概念,sin值是 对边/斜边 得到,cos值是 邻边/斜边得到,要求出这个点的坐标就是求出这个三角形的两条边,一条边的长度为X,另一条的长度为Y。现在我们已经知道的是斜边的长度,要求出这两条边,就要先知道弧度值

    我们先来考虑最长的秒针,它每一秒都会移动一个角度,这个角度我们可以计算出来:
    360/60=6
    得到6度,这不难理解,因为整个圆是360度,一般的时钟在相邻小时之间又有5个刻度,所以一共是12*5=60个刻度,然后用360/60=6得到秒针一秒走6度,在程序里必须要将角度转化成弧度才能进行sin运算,所以还要乘以 PI/180(PI为圆周率)

    有了这个弧度值,我们就可以得到每一秒秒针的顶点坐标了(以在第一区间考虑):
    sec_X = centerPoint.X + sin(6*PI/180)*当前秒钟值*s_Len
    sce_Y = centerPoint.Y - cos(6*PI/180)*当前秒钟值*s_Len

    接下来处理时针,因为我们这只是个很简单的模拟,所以就不需要对细节作过多的处理,就让它每过一分钟就跳到下一个刻度,这样求时针的顶点坐标和求秒针的就是一样的算法
    min_X = centerPoint.X + sin(6*PI/180)*当前分钟值*m_Len
    min_Y = centerPoint.Y - cos(6*PI/180)*当前分钟值*m_Len

    时针的处理方式要不同,因为时针的取值只有12个数字,而分钟和秒针都有60个数字,如果以之前的方式处理时针,效果就是时针到点的那一瞬间它一下子转30度,当然不会有这样效果的时钟。那么我们该如何处理这个棘手的时针呢?

    假如现在是06:45:30,在整6点时它的度数是30*6,整7点是30*7,为了不至于将改变全在最后刻发生,我们加入时针值来参与计算,这样当前时间就是h*30+m/2,m为60时 m/2=30 ,也就是 m/2这个值在0--30之间,当角度变化为30度时,就指到了下一个整点

    这样我们得到了时针的算法
    hour_X = centerPoint.X + sin((h*30+m/2)*PI/180)*h_Len
    hour_Y = centerPoint.Y - cos((h*30+m/2)*PI/180)*h_Len

    最关键的代码处理了,剩下的工作就简单了,我们在程序里加入一个标签lblTime,来显示当前时间

    string TimeInString = "";
    int hour = DateTime.Now.Hour;
    int min = DateTime.Now.Minute;
    int sec = DateTime.Now.Second;
    TimeInString = (hour<10)? "0"+hour.ToString():hour.ToString();
    TimeInString += (min<10)? "0"+min.ToString():min.ToString();
    TimeInString == (sec<10)? "0"+sec.ToString():sec.ToString();
    lblTime.Text = TimeInString;

  • 相关阅读:
    【阅读】读书只是生活方式的一种
    【Android崩溃记录】持续更新(bugly)
    【iOS】判断字符串是否是url及从字符串中提取url
    【React Native】正则判断字符串中是否包含url、提取字符串中的url
    【Objective-C】自定义UITextView(placeholder,长按换行,文字位置、文字间距等)
    【Swift】监听耳机插入拔出的通知
    INS(Instagram)如何绑定谷歌二次验证码/谷歌身份验证/双重认证?
    脸书(Facebook)如何绑定谷歌二次验证码/谷歌身份验证/双重认证?
    推特(Twitter)如何绑定谷歌二次验证码/谷歌身份验证/双重认证?
    TeamViewer如何绑定谷歌二次验证码/谷歌身份验证?
  • 原文地址:https://www.cnblogs.com/xiexingen/p/2755859.html
Copyright © 2020-2023  润新知