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!

  整个流程就是这样,有疑问的朋友,欢迎留言!

 

下载地址,附源码 ==》 

 

支持原创,转载请标明出处,谢谢!