FanControl.cs 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. using Avalonia;
  2. using Avalonia.Controls;
  3. using Avalonia.Media;
  4. using Avalonia.Threading;
  5. using System;
  6. using System.Globalization;
  7. namespace YZWater.Avalonia.Controls;
  8. public class FanControl : Control
  9. {
  10. public static readonly StyledProperty<bool> IsRunningProperty =
  11. AvaloniaProperty.Register<FanControl, bool>(nameof(IsRunning), false);
  12. public static readonly StyledProperty<double> SpeedProperty =
  13. AvaloniaProperty.Register<FanControl, double>(nameof(Speed), 1.0);
  14. public static readonly StyledProperty<string> TextProperty =
  15. AvaloniaProperty.Register<FanControl, string>(nameof(Text), "椋庢墖");
  16. public bool IsRunning { get => GetValue(IsRunningProperty); set => SetValue(IsRunningProperty, value); }
  17. public double Speed { get => GetValue(SpeedProperty); set => SetValue(SpeedProperty, value); }
  18. public string Text { get => GetValue(TextProperty); set => SetValue(TextProperty, value); }
  19. private double _angle;
  20. private IDisposable? _timer;
  21. static FanControl() { AffectsRender<FanControl>(IsRunningProperty, SpeedProperty, TextProperty); }
  22. protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); if (IsRunning) StartAnim(); }
  23. protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); StopAnim(); }
  24. protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
  25. {
  26. base.OnPropertyChanged(change);
  27. if (change.Property == IsRunningProperty) { if (IsRunning) StartAnim(); else { StopAnim(); _angle = 0; } }
  28. }
  29. private void StartAnim() { StopAnim(); _timer = DispatcherTimer.Run(() => { _angle += Speed * 2; if (_angle >= 360) _angle -= 360; InvalidateVisual(); return true; }, TimeSpan.FromMilliseconds(50)); }
  30. private void StopAnim() { _timer?.Dispose(); _timer = null; }
  31. public override void Render(DrawingContext context)
  32. {
  33. base.Render(context);
  34. var bounds = new Rect(Bounds.Size);
  35. var cx = bounds.Width / 2; var cy = bounds.Height / 2;
  36. var radius = Math.Min(bounds.Width, bounds.Height) / 2 - 5;
  37. var typeface = new Typeface("Microsoft YaHei", FontStyle.Normal, FontWeight.Bold);
  38. var fanColor = IsRunning ? ThemeHelper.Success() : ThemeHelper.TextDisabled();
  39. // 澶栨
  40. context.DrawRectangle(null, new Pen(ThemeHelper.Border(), 2), new Rect(cx - radius, cy - radius, radius * 2, radius * 2));
  41. // 鍙剁墖
  42. var bladeCount = 4;
  43. var bladeLen = radius * 0.8;
  44. var bladeW = radius * 0.25;
  45. for (int i = 0; i < bladeCount; i++)
  46. {
  47. var angle = _angle + (360.0 / bladeCount) * i;
  48. var bx = cx + Math.Cos(angle * Math.PI / 180) * bladeLen * 0.4;
  49. var by = cy + Math.Sin(angle * Math.PI / 180) * bladeLen * 0.4;
  50. context.DrawRectangle(fanColor, null, new Rect(bx - bladeW / 2, by - bladeLen / 2, bladeW, bladeLen));
  51. }
  52. // 涓績
  53. var cr = radius * 0.15;
  54. context.DrawEllipse(fanColor, null, new Rect(cx - cr, cy - cr, cr * 2, cr * 2));
  55. // 鏂囧瓧
  56. if (!string.IsNullOrEmpty(Text))
  57. {
  58. var t = new FormattedText(Text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, 10, ThemeHelper.TextPrimary());
  59. context.DrawText(t, new Point(cx - t.Width / 2, bounds.Bottom - t.Height - 2));
  60. }
  61. }
  62. protected override Size MeasureOverride(Size availableSize) => new Size(50, 50);
  63. }