wpf 模拟抖音很火的罗盘时钟,附源码
前端时间突然发现,抖音火了个壁纸,就是黑底蕾丝~~~ 错错错,黑底白字的罗盘时钟!
作为程序员的我,也觉得很新颖,所以想空了研究下,这不,空下来了就用wpf,写个属于.net自己的罗盘时钟,目前只实现了时分秒,农历日期等逻辑都是一样的,所以就略了,有兴趣的朋友,可以继续深入!
最开始想直接弄成成exe,方便拷贝,到处运行使用的,但是考虑到,万一有网友朋友们需要,所以我还是把封成一个dll,需要的地方添加引用即可!
为了弄这个,还恶补了下,高中还是初中的知识,sin30,cos60,呵呵,正弦,余弦,所以不明白的朋友们需要先了解清楚这个,因为罗盘是旋转,需要用到计算这个值!
不废话了,先上图看下效果!
ok,整体效果就是这样了,中间是鄙人的名称缩写,抖音上是很潦草的,我就随便啦,占个位置,不然显得很空洞!
下面说说代码
主要是,romeclockcontrollibrary,这个是对控件的封装,上面那个启动程序只是一个容器,或者说是调用者,当然,如果要达到我这个效果,实现圆形的窗口透明的朋友们,可以看下借鉴!
<usercontrol x:class="romeclockcontrollibrary.romeclockcontrol" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:romeclockcontrollibrary" mc:ignorable="d" d:designheight="450" d:designwidth="800" > <border x:name="bor" background="#000000" > <grid x:name="grid" > </grid> </border> </usercontrol>
上面是前端代码,有点基础的都应该看得懂,没什么可说的
using system; using system.collections.generic; using system.diagnostics; using system.linq; using system.text; using system.threading; using system.threading.tasks; using system.windows; using system.windows.controls; using system.windows.data; using system.windows.documents; using system.windows.input; using system.windows.media; using system.windows.media.animation; using system.windows.media.imaging; using system.windows.navigation; using system.windows.shapes; namespace romeclockcontrollibrary { /// <summary> /// 罗马时钟 /// </summary> public partial class romeclockcontrol : usercontrol, idisposable { public romeclockcontrol() { initializecomponent(); } /// <summary> /// x轴的中心位置 /// </summary> private double centerpixtox => this.actualwidth / 2; /// <summary> /// y轴的中心位置 /// </summary> private double centerpixtoy => this.actualheight / 2; /// <summary> /// 秒 /// </summary> private canvas canvashour = null; /// <summary> /// 分 /// </summary> private canvas canvasminute = null; /// <summary> /// 时 /// </summary> private canvas canvassecond = null; /// <summary> /// ui更新线程 /// </summary> private thread thread = null; /// <summary> /// 缓存时的显示控件 /// </summary> private textblock blockhour = null; /// <summary> /// 缓存分的显示控件 /// </summary> private textblock blockminute = null; /// <summary> /// 缓存秒的显示控件 /// </summary> private textblock blocksecond = null; /// <summary> /// 添加控件 /// </summary> private void add(addtype type) { var offset = 0;//偏移量 var count = 0;//总量 var str = string.empty; var time = 0; double angletime = 0; canvas canvas = new canvas(); canvas.tag = type; switch (type) { case addtype.hour: offset = 95; count = 24; str = "时"; canvashour = canvas; time = datetime.now.hour; break; case addtype.minute: offset = 60; count = 60; str = "分"; canvasminute = canvas; time = datetime.now.minute; break; case addtype.second: offset = 30; count = 60; str = "秒"; canvassecond = canvas; time = datetime.now.second; break; default: return; } var angle = 360 / count;//角度 var x = centerpixtox - offset;//起始位置 var y = centerpixtoy - offset; for (int i = 0; i < count; i++) { textblock t = new textblock(); if (i <= 9) { t.text = $"0{i}{str}"; } else { t.text = $"{i}{str}"; } t.tag = i; t.foreground = new solidcolorbrush((color)colorconverter.convertfromstring("#7d7d7d")); canvas.children.add(t); var sinv = math.sin((90 - angle * i) * (math.pi / 180)); var cosv = math.cos((90 - angle * i) * (math.pi / 180)); var a = centerpixtoy - y * sinv; var b = centerpixtox + y * cosv; canvas.setleft(t, b); canvas.settop(t, a); //设置角度 rotatetransform r = new rotatetransform(); r.angle = angle * i - 90; t.rendertransform = r; if (i == time) { angletime = 360 - r.angle; //更新样式 t.foreground = new solidcolorbrush(colors.white); switch (type) { case addtype.hour: blockhour = t; break; case addtype.minute: blockminute = t; break; case addtype.second: blocksecond = t; break; } } } grid.children.add(canvas); //获取当前时间,旋转对应的角度 rotatetransform rtf = new rotatetransform(); rtf.centerx = centerpixtox; rtf.centery = centerpixtoy; rtf.angle = angletime; canvas.rendertransform = rtf; } /// <summary> /// 渲染时钟 /// </summary> public void show() { dispose(); //设置圆角 bor.cornerradius = new cornerradius(this.actualwidth / 2); add(addtype.hour); add(addtype.minute); add(addtype.second); addname(); thread = new thread(new threadstart(threadmethod)); thread.isbackground = true; thread.start(); } /// <summary> /// 生成名称 /// </summary> private void addname() { textblock tb = new textblock(); tb.text = "xl"; tb.foreground = new solidcolorbrush(colors.white); tb.fontsize = 60; tb.fontfamily = new fontfamily("华文琥珀"); tb.horizontalalignment = horizontalalignment.center; tb.verticalalignment = verticalalignment.center; grid.children.add(tb); } /// <summary> /// ui更新线程 /// </summary> private void threadmethod() { while (true) { dispatcher.invoke(() => { var s = datetime.now.second; var m = datetime.now.minute; var h = datetime.now.hour; //处理时 if (m == 0 && (int)blockhour.tag != h) { setui(canvashour, h); } //处理分 if (s == 0 && (int)blockminute.tag != m) { setui(canvasminute, m); } //处理秒 setui(canvassecond, s); }); thread.sleep(1000); } } /// <summary> /// 更新ui /// </summary> /// <param name="can"></param> /// <param name="tag"></param> /// <param name="color"></param> private void setui(canvas can, int tag) { var type = (addtype)can.tag; foreach (textblock item in can.children) { if ((int)item.tag == tag) { debug.writeline(item.text); var fr = item.rendertransform as rotatetransform; var angle = 360 - fr.angle; var rtf = can.rendertransform as rotatetransform; doubleanimation db = null; if (type == addtype.minute) { angle -= 360; db = new doubleanimation(angle, new duration(timespan.fromseconds(1))); db.completed += dbminute_completed; blockminute = item; } else if (type == addtype.hour) { angle += 360; db = new doubleanimation(angle, new duration(timespan.fromseconds(1.5))); db.completed += dbhour_completed; blockhour = item; } else { db = new doubleanimation(angle, new duration(timespan.fromseconds(0.25))); db.completed += dbsecond_completed; blocksecond = item; } rtf.beginanimation(rotatetransform.angleproperty, db); } else { item.foreground = new solidcolorbrush((color)colorconverter.convertfromstring("#7d7d7d")); } } } /// <summary> /// 秒 动画完成时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void dbsecond_completed(object sender, eventargs e) { blocksecond.foreground = new solidcolorbrush(colors.white); } /// <summary> /// 时 动画完成时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void dbhour_completed(object sender, eventargs e) { var fr = canvashour.rendertransform as rotatetransform; var angle = fr.angle - 360; fr = null; rotatetransform rtf = new rotatetransform(); rtf.centerx = centerpixtox; rtf.centery = centerpixtoy; rtf.angle = angle; canvashour.rendertransform = rtf; debug.writeline(rtf.angle); blockhour.foreground = new solidcolorbrush(colors.white); } /// <summary> /// 分 动画完成时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void dbminute_completed(object sender, eventargs e) { var fr = canvasminute.rendertransform as rotatetransform; var angle = fr.angle + 360; fr = null; rotatetransform rtf = new rotatetransform(); rtf.centerx = centerpixtox; rtf.centery = centerpixtoy; rtf.angle = angle; canvasminute.rendertransform = rtf; debug.writeline(rtf.angle); blockminute.foreground = new solidcolorbrush(colors.white); } /// <summary> /// 释放 /// </summary> public void dispose() { thread?.abort(); grid.children.clear(); } } /// <summary> /// 添加类型 /// </summary> public enum addtype { hour, minute, second } }
上面是后端逻辑,这才是重点,调用者通过show,调用显示的;在show里面会开启一个后台处理线程,来实现获取当前时间,并计算需要旋转的角度,最后采用动画更新到ui!
整个流程就是这样,有疑问的朋友,欢迎留言!
下载地址,附源码 ==》
支持原创,转载请标明出处,谢谢!
黄山市民网:https://www.huangshanshimin.com/