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!
整個流程就是這樣,有疑問的朋友,歡迎留言!
下載地址,附源碼 ==》 點擊我前往
支持原創,轉載請標明出處,謝謝!
