Files
qhmes/yy-admin-master/YY.Admin.Core/Controls/FontAwesomeIcon.cs

300 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using PropertyMetadata = System.Windows.PropertyMetadata;
namespace YY.Admin.Core.Controls
{
//图标官网
public enum FontAwesomeStyle
{
/// <summary>
/// 常规
/// </summary>
Regular,
/// <summary>
/// 实心的
/// </summary>
Solid,
/// <summary>
/// 品牌Logo
/// </summary>
Brands
}
public class FontAwesomeIcon : TextBlock
{
#region Icon
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register(nameof(Icon), typeof(string), typeof(FontAwesomeIcon),
new PropertyMetadata(null, (d, e) => ((FontAwesomeIcon)d).Text = e.NewValue?.ToString()));
public string Icon
{
get => (string)GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
#endregion
#region Spin
public static readonly DependencyProperty SpinProperty =
DependencyProperty.Register(nameof(Spin), typeof(bool), typeof(FontAwesomeIcon),
new PropertyMetadata(false, (d, e) => ((FontAwesomeIcon)d).UpdateAnimation()));
public bool Spin
{
get => (bool)GetValue(SpinProperty);
set => SetValue(SpinProperty, value);
}
#endregion
#region SpinSpeed
public static readonly DependencyProperty SpinSpeedProperty =
DependencyProperty.Register(nameof(SpinSpeed), typeof(double), typeof(FontAwesomeIcon),
new PropertyMetadata(1.0, (d, e) =>
{
var fa = (FontAwesomeIcon)d;
if (fa.Spin)
fa.StartSpin();
}));
public double SpinSpeed
{
get => (double)GetValue(SpinSpeedProperty);
set => SetValue(SpinSpeedProperty, value);
}
#endregion
#region Beat
public static readonly DependencyProperty BeatProperty =
DependencyProperty.Register(nameof(Beat), typeof(bool), typeof(FontAwesomeIcon),
new PropertyMetadata(false, (d, e) => ((FontAwesomeIcon)d).UpdateAnimation()));
public bool Beat
{
get => (bool)GetValue(BeatProperty);
set => SetValue(BeatProperty, value);
}
#endregion
#region BeatScale
public static readonly DependencyProperty BeatScaleProperty =
DependencyProperty.Register(nameof(BeatScale), typeof(double), typeof(FontAwesomeIcon),
new PropertyMetadata(1.3));
public double BeatScale
{
get => (double)GetValue(BeatScaleProperty);
set => SetValue(BeatScaleProperty, value);
}
#endregion
#region BeatDuration
public static readonly DependencyProperty BeatDurationProperty =
DependencyProperty.Register(nameof(BeatDuration), typeof(double), typeof(FontAwesomeIcon),
new PropertyMetadata(0.5));
public double BeatDuration
{
get => (double)GetValue(BeatDurationProperty);
set => SetValue(BeatDurationProperty, value);
}
#endregion
#region IconFamily
public static readonly DependencyProperty IconFamilyProperty =
DependencyProperty.Register(nameof(IconFamily), typeof(FontAwesomeStyle), typeof(FontAwesomeIcon),
new PropertyMetadata(FontAwesomeStyle.Regular, OnIconFamilyChanged));
public FontAwesomeStyle IconFamily
{
get => (FontAwesomeStyle)GetValue(IconFamilyProperty);
set => SetValue(IconFamilyProperty, value);
}
private static void OnIconFamilyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var fa = (FontAwesomeIcon)d;
fa.UpdateFontFamily();
}
private void UpdateFontFamily()
{
FontFamily = IconFamily switch
{
FontAwesomeStyle.Regular => (FontFamily)Application.Current.Resources["FontAwesomeRegular"],
FontAwesomeStyle.Solid => (FontFamily)Application.Current.Resources["FontAwesomeSolid"],
FontAwesomeStyle.Brands => (FontFamily)Application.Current.Resources["FontAwesomeBrands"],
_ => (FontFamily)Application.Current.Resources["FontAwesomeRegular"]
};
}
#endregion
// Transform components
private RotateTransform? _rotateTransform;
private ScaleTransform? _scaleTransform;
private TransformGroup? _transformGroup;
private bool _initialized = false;
private DoubleAnimation? _currentSpinAnimation;
public FontAwesomeIcon()
{
// 设置文本对齐方式,确保内容居中
TextAlignment = TextAlignment.Center;
VerticalAlignment = VerticalAlignment.Center;
HorizontalAlignment = HorizontalAlignment.Center;
// 设置默认字体家庭
UpdateFontFamily();
// 创建 transforms
_rotateTransform = new RotateTransform();
_scaleTransform = new ScaleTransform(1, 1);
_transformGroup = new TransformGroup();
_transformGroup.Children.Add(_scaleTransform);
_transformGroup.Children.Add(_rotateTransform);
// 使用 RenderTransform 避免布局抖动
RenderTransform = _transformGroup;
// 关键:设置变换原点为内容中心
RenderTransformOrigin = new Point(0.5, 0.5);
// 延迟初始化到 Loaded
Loaded += OnLoadedSafe;
Unloaded += OnUnloadedSafe;
SizeChanged += OnSizeChangedSafe;
}
private void OnLoadedSafe(object? sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke((Action)(() =>
{
if (_initialized) return;
_initialized = true;
UpdateAnimation();
}), DispatcherPriority.Loaded);
}
private void OnSizeChangedSafe(object? sender, SizeChangedEventArgs e)
{
// 确保变换原点始终居中
RenderTransformOrigin = new Point(0.5, 0.5);
}
private void OnUnloadedSafe(object? sender, RoutedEventArgs e)
{
// 停止动画,避免内存泄漏
StopSpin();
StopBeat();
}
private void UpdateAnimation()
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
if (!_initialized)
{
return;
}
if (Spin) StartSpin(); else StopSpin();
if (Beat) StartBeat(); else StopBeat();
}
private void StartSpin()
{
if (_rotateTransform == null) return;
// 重新确认 RenderTransform 已正确赋值
if (RenderTransform != _transformGroup)
{
RenderTransform = _transformGroup;
RenderTransformOrigin = new Point(0.5, 0.5);
}
// 先取消旧动画
StopSpin();
// 方法1使用 By 动画实现无缝旋转(推荐)
var anim = new DoubleAnimation
{
By = 360, // 每次增加360度
Duration = TimeSpan.FromSeconds(Math.Max(0.01, SpinSpeed)),
RepeatBehavior = RepeatBehavior.Forever
};
// 动画在 WPF 内部直接使用 Freezable 缓存,提高性能
anim.Freeze();
_currentSpinAnimation = anim;
_rotateTransform.BeginAnimation(RotateTransform.AngleProperty, anim);
// 方法2使用 IsCumulative 属性(备选方案)
/*
var anim = new DoubleAnimation(0, 360, new Duration(TimeSpan.FromSeconds(Math.Max(0.01, SpinSpeed))))
{
RepeatBehavior = RepeatBehavior.Forever,
IsCumulative = true // 累积值避免跳回0度
};
*/
}
private void StopSpin()
{
_rotateTransform?.BeginAnimation(RotateTransform.AngleProperty, null);
_currentSpinAnimation = null;
// 重置旋转角度
if (_rotateTransform != null)
_rotateTransform.Angle = 0;
}
private void StartBeat()
{
if (_scaleTransform == null)
{
return;
}
// 重新确认 RenderTransform 已正确赋值
if (RenderTransform != _transformGroup)
{
RenderTransform = _transformGroup;
RenderTransformOrigin = new Point(0.5, 0.5);
}
// 先停止已有缩放动画
_scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, null);
_scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, null);
// 使用更平滑的缓动函数
var anim = new DoubleAnimation(1.0, BeatScale, new Duration(TimeSpan.FromSeconds(Math.Max(0.01, BeatDuration))))
{
AutoReverse = true,
RepeatBehavior = RepeatBehavior.Forever,
EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseInOut }
};
_scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, anim);
_scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, anim);
}
private void StopBeat()
{
if (_scaleTransform != null)
{
_scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, null);
_scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, null);
_scaleTransform.ScaleX = 1.0;
_scaleTransform.ScaleY = 1.0;
}
}
}
}