C#自定义WPF ColorAnimation:实现用户自定义颜色绑定及效果优化
WPF颜色动画To属性绑定自定义颜色的优化方案
问题描述
我在WPF资源字典里有大量带颜色动画故事板的样式和控件模板,需要把这些颜色动画的To属性绑定到用户选择的任意颜色。之前找到的方案只适用于Double动画,于是自己写了一段自定义颜色动画的代码,但效果不如WPF原生颜色动画,想找更优实现或让自定义效果贴近原生的方法。
用户自定义实现代码:
SolidColorBrush brush = new SolidColorBrush(System.Windows.Media.Color.FromRgb(0,0,0)); if (values[1] is string && values[2] is string && values[0] is double) { System.Drawing.Color color = ColorTranslator.FromHtml(values[1].ToString()); double r = System.Convert.ToInt16(color.R); double g = System.Convert.ToInt16(color.G); double b = System.Convert.ToInt16(color.B); System.Drawing.Color color2 = ColorTranslator.FromHtml(values[2].ToString()); double r2 = System.Convert.ToInt16(color2.R); double g2 = System.Convert.ToInt16(color2.G); double b2 = System.Convert.ToInt16(color2.B); int r3 = System.Convert.ToInt32(r + ((r2 - r) * (double)values[0])); int g3 = System.Convert.ToInt32(g + ((g2 - g) * (double)values[0])); int b3 = System.Convert.ToInt32(b + ((b2 - b) * (double)values[0])); brush = new SolidColorBrush(System.Windows.Media.Color.FromRgb(System.Convert.ToByte(r3), System.Convert.ToByte(g3), System.Convert.ToByte(b3))); } return brush;
优化方案
1. 直接用ColorAnimationUsingKeyFrames实现绑定(推荐)
原生ColorAnimation不支持直接绑定To属性,但ColorAnimationUsingKeyFrames可以通过关键帧绑定动态颜色,完全复用WPF原生插值逻辑,效果和原生动画一致:
<Storyboard> <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" Duration="0:0:0.3"> <!-- 起始颜色可以绑定静态资源或初始值 --> <LinearColorKeyFrame KeyTime="0:0:0" Value="{StaticResource DefaultBackgroundColor}"/> <!-- 目标颜色绑定用户选择的颜色 --> <LinearColorKeyFrame KeyTime="0:0:0.3" Value="{Binding UserSelectedColor}"/> </ColorAnimationUsingKeyFrames> </Storyboard>
2. 修复自定义动画的色彩空间插值逻辑
你的自定义实现效果差的核心原因是直接对RGB分量做线性插值,而WPF原生动画是在sRGB色彩空间(带伽马校正)插值。优化后代码如下:
// sRGB转线性空间(伽马校正) private double SrgbToLinear(byte component) { double c = component / 255.0; return c <= 0.04045 ? c / 12.92 : Math.Pow((c + 0.055) / 1.055, 2.4); } // 线性空间转回sRGB private byte LinearToSrgb(double value) { double c = value <= 0.0031308 ? value * 12.92 : 1.055 * Math.Pow(value, 1/2.4) - 0.055; return (byte)Math.Clamp(Math.Round(c * 255), 0, 255); } // 优化后的插值方法 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values[0] is double progress && values[1] is string startColorStr && values[2] is string endColorStr) { var startColor = System.Drawing.ColorTranslator.FromHtml(startColorStr); var endColor = System.Drawing.ColorTranslator.FromHtml(endColorStr); // 转线性空间插值 double rLinear = SrgbToLinear(startColor.R) + (SrgbToLinear(endColor.R) - SrgbToLinear(startColor.R)) * progress; double gLinear = SrgbToLinear(startColor.G) + (SrgbToLinear(endColor.G) - SrgbToLinear(startColor.G)) * progress; double bLinear = SrgbToLinear(startColor.B) + (SrgbToLinear(endColor.B) - SrgbToLinear(startColor.B)) * progress; // 转回sRGB byte r = LinearToSrgb(rLinear); byte g = LinearToSrgb(gLinear); byte b = LinearToSrgb(bLinear); return new SolidColorBrush(System.Windows.Media.Color.FromRgb(r, g, b)); } return new SolidColorBrush(System.Windows.Media.Colors.Black); }
3. 自定义可绑定的AnimationTimeline
继承AnimationTimeline实现支持绑定的颜色动画,语法和原生动画一致,同时保留原生插值效果:
public class BindableColorAnimation : AnimationTimeline { public static readonly DependencyProperty FromProperty = DependencyProperty.Register("From", typeof(Color?), typeof(BindableColorAnimation)); public static readonly DependencyProperty ToProperty = DependencyProperty.Register("To", typeof(Color?), typeof(BindableColorAnimation)); public Color? From { get => (Color?)GetValue(FromProperty); set => SetValue(FromProperty, value); } public Color? To { get => (Color?)GetValue(ToProperty); set => SetValue(ToProperty, value); } public override Type TargetPropertyType => typeof(Color); protected override Freezable CreateInstanceCore() => new BindableColorAnimation(); public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock) { var progress = animationClock.CurrentProgress.Value; var from = From ?? (Color)defaultOriginValue; var to = To ?? (Color)defaultDestinationValue; // 复用WPF原生颜色插值逻辑 return Color.FromArgb( (byte)(from.A + (to.A - from.A) * progress), (byte)(from.R + (to.R - from.R) * progress), (byte)(from.G + (to.G - from.G) * progress), (byte)(from.B + (to.B - from.B) * progress) ); } }
XAML使用示例:
<local:BindableColorAnimation Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)" From="#FF333333" To="{Binding UserSelectedTextColor}" Duration="0:0:0.2"/>
内容的提问来源于stack exchange,提问作者megahendrik
相关产品推荐
相关产品推荐

