wpf 模擬抖音很火的羅盤時鐘,附源碼 前端時間突然發現,抖音火了個壁紙,就是黑底蕾絲~~~ 錯錯錯,黑底白字的羅盤時鐘! 作為程式員的我,也覺得很新穎,所以想空了研究下,這不,空下來了就用wpf,寫個屬於.net自己的羅盤時鐘,目前只實現了時分秒,農曆日期等邏輯都是一樣的,所以就略了,有興趣的朋友 ...
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!
整個流程就是這樣,有疑問的朋友,歡迎留言!
下載地址,附源碼 ==》 點擊我前往
支持原創,轉載請標明出處,謝謝!