如何在WinUI-3 C#的Shimmer控件中实现波浪动画?
WinUI 3 Shimmer 控件波浪动画实现方案
针对已搭建的Shimmer控件结构,以下是实现波浪动画的具体方案,包含XAML模板修改、动画逻辑整合以及自定义属性的绑定处理:
一、修改ControlTemplate 添加动画层
将原静态填充的椭圆和矩形替换为带渐变刷的动态元素,并添加VisualState管理动画状态:
<Style x:Key="ShimmerStyle" TargetType="local:Shimmer"> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="Fill" Value="{ThemeResource ShimmerFillColor}"/> <Setter Property="WaveColor" Value="#FFFFFF"/> <Setter Property="AnimationDuration" Value="0:0:1.5"/> <Setter Property="WaveDirection" Value="Horizontal"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:Shimmer"> <StackPanel> <Grid x:Name="Grid1"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="ActiveStates"> <VisualState x:Name="Active"> <Storyboard RepeatBehavior="Forever"> <DoubleAnimation Storyboard.TargetName="EliGradientTransform" Storyboard.TargetProperty="X" From="-1" To="1" Duration="{TemplateBinding AnimationDuration}"/> <DoubleAnimation Storyboard.TargetName="RectGradientTransform" Storyboard.TargetProperty="X" From="-1" To="1" Duration="{TemplateBinding AnimationDuration}"/> <DoubleAnimation Storyboard.TargetName="Rect1GradientTransform" Storyboard.TargetProperty="X" From="-1" To="1" Duration="{TemplateBinding AnimationDuration}"/> </Storyboard> </VisualState> <VisualState x:Name="Inactive"/> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Ellipse x:Name="Eli" Grid.Row="0" Grid.Column="0" Height="200" Width="200" HorizontalAlignment="Left"> <Ellipse.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="1,0"> <GradientStop Color="{TemplateBinding Fill}" Offset="0"/> <GradientStop Color="{TemplateBinding Fill}" Offset="0.4"/> <GradientStop Color="{TemplateBinding WaveColor}" Offset="0.5"/> <GradientStop Color="{TemplateBinding Fill}" Offset="0.6"/> <GradientStop Color="{TemplateBinding Fill}" Offset="1"/> <LinearGradientBrush.Transform> <TranslateTransform x:Name="EliGradientTransform" X="-1"/> </LinearGradientBrush.Transform> </LinearGradientBrush> </Ellipse.Fill> </Ellipse> <Rectangle x:Name="Rect" Margin="40,30,50,100" Grid.Row="0" Grid.Column="1" Height="60" Width="800" Opacity="0.8"> <Rectangle.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="1,0"> <GradientStop Color="{TemplateBinding Fill}" Offset="0"/> <GradientStop Color="{TemplateBinding Fill}" Offset="0.4"/> <GradientStop Color="{TemplateBinding WaveColor}" Offset="0.5"/> <GradientStop Color="{TemplateBinding Fill}" Offset="0.6"/> <GradientStop Color="{TemplateBinding Fill}" Offset="1"/> <LinearGradientBrush.Transform> <TranslateTransform x:Name="RectGradientTransform" X="-1"/> </LinearGradientBrush.Transform> </LinearGradientBrush> </Rectangle.Fill> </Rectangle> <Rectangle x:Name="Rect1" Grid.Row="0" Grid.Column="1" Margin="40,90,0,0" Height="60" Width="300" HorizontalAlignment="Left"> <Rectangle.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="1,0"> <GradientStop Color="{TemplateBinding Fill}" Offset="0"/> <GradientStop Color="{TemplateBinding Fill}" Offset="0.4"/> <GradientStop Color="{TemplateBinding WaveColor}" Offset="0.5"/> <GradientStop Color="{TemplateBinding Fill}" Offset="0.6"/> <GradientStop Color="{TemplateBinding Fill}" Offset="1"/> <LinearGradientBrush.Transform> <TranslateTransform x:Name="Rect1GradientTransform" X="-1"/> </LinearGradientBrush.Transform> </LinearGradientBrush> </Rectangle.Fill> </Rectangle> </Grid> <Grid x:Name="Grid2"> <ContentPresenter Content="{TemplateBinding Content}"/> </Grid> </StackPanel> <ControlTemplate.Triggers> <Trigger Property="IsActive" Value="True"> <Setter TargetName="Grid1" Property="Visibility" Value="Visible"/> <Setter TargetName="Grid2" Property="Visibility" Value="Collapsed"/> <Setter Property="VisualStateManager.GoToState" TargetName="Grid1" Value="Active"/> </Trigger> <Trigger Property="IsActive" Value="False"> <Setter TargetName="Grid1" Property="Visibility" Value="Collapsed"/> <Setter TargetName="Grid2" Property="Visibility" Value="Visible"/> <Setter Property="VisualStateManager.GoToState" TargetName="Grid1" Value="Inactive"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style BasedOn="{StaticResource ShimmerStyle}" TargetType="local:Shimmer"/>
二、核心逻辑说明
- 渐变刷与平移动画:每个元素使用包含基础填充色和波浪色的线性渐变刷,通过平移变换(
TranslateTransform)的X轴偏移动画,实现亮条从左到右扫过的效果。 - 状态切换:通过
IsActive属性触发ControlTemplate触发器,切换Shimmer骨架和内容区域的可见性,并启动/停止动画。 - 自定义属性绑定:
WaveColor绑定到渐变的亮条颜色,AnimationDuration直接控制动画时长,确保控件的可配置性。
三、扩展WaveDirection属性支持
若要实现垂直方向的波浪动画,可在后台类中添加属性变更回调,动态调整渐变刷方向和动画目标:
public enum ShimmerDirection { Horizontal, Vertical } public static readonly DependencyProperty WaveDirectionProperty = DependencyProperty.Register( nameof(WaveDirection), typeof(ShimmerDirection), typeof(Shimmer), new PropertyMetadata(ShimmerDirection.Horizontal, OnWaveDirectionChanged)); public ShimmerDirection WaveDirection { get => (ShimmerDirection)GetValue(WaveDirectionProperty); set => SetValue(WaveDirectionProperty, value); } private static void OnWaveDirectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is Shimmer shimmer && shimmer.GetTemplateChild("Grid1") is Grid grid1) { var targetDirection = (ShimmerDirection)e.NewValue; UpdateElementGradientDirection(grid1.FindName("Eli") as Ellipse, targetDirection); UpdateElementGradientDirection(grid1.FindName("Rect") as Rectangle, targetDirection); UpdateElementGradientDirection(grid1.FindName("Rect1") as Rectangle, targetDirection); UpdateAnimationTargetProperty(shimmer, targetDirection); } } private static void UpdateElementGradientDirection(Shape shape, ShimmerDirection direction) { if (shape?.Fill is LinearGradientBrush brush) { if (direction == ShimmerDirection.Vertical) { brush.StartPoint = new Point(0, 0); brush.EndPoint = new Point(0, 1); } else { brush.StartPoint = new Point(0, 0); brush.EndPoint = new Point(1, 0); } } } private static void UpdateAnimationTargetProperty(Shimmer shimmer, ShimmerDirection direction) { if (shimmer.GetTemplateChild("ActiveStates") is VisualStateGroup stateGroup) { var activeState = stateGroup.States.FirstOrDefault(s => s.Name == "Active"); if (activeState?.Storyboard is Storyboard storyboard) { foreach (var animation in storyboard.Children.OfType<DoubleAnimation>()) { animation.TargetProperty = direction == ShimmerDirection.Vertical ? "Y" : "X"; animation.From = direction == ShimmerDirection.Vertical ? -1 : -1; animation.To = direction == ShimmerDirection.Vertical ? 1 : 1; } } } }
四、注意事项
- 确保项目中已引用
Microsoft.UI.Xaml.Controls,并定义BooleanToVisibilityConverter(或使用内置转换器)。 - 若波浪效果不明显,可调整渐变停止点的Offset值(如将0.4/0.6改为0.3/0.7),或修改
WaveColor的透明度增强对比度。 - 动画启动后会循环播放,直到
IsActive设为False,此时动画自动停止并切换到内容显示。
内容的提问来源于stack exchange,提问作者Budding developer
相关产品推荐
相关产品推荐

