一、WPF动画基础
1、动画本质
通过随时间改变依赖属性值实现视觉效果(如位置、透明度、颜色等)。
依赖属性必须支持 DependencyProperty,且需是可动画的(如 Double, Color, Point 等)。
2、动画三要素
- 起始值 (From)
- 结束值 (To)
- 持续时间 (Duration)
3 、基本动画类型
|-----------------------|-----------|-----------------|----------------------|
| 类型 | 描述 | 示例属性 | 对应动画类 |
| 线性动画 | 线性插值变化 | Width, Opacity | DoubleAnimation |
| 颜色动画 | 颜色渐变 | Background | ColorAnimation |
| 点 / 路径动画 | 沿路径或坐标点移动 | Canvas.Left/Top | PointAnimation |
| 旋转 / 缩放动画 | 变换效果 | RenderTransform | RotateTransform + 动画 |
二、WPF动画基础类型
1、线性插值动画
- 原理:通过起始值和结束值的线性过渡实现平滑动画(如DoubleAnimation控制宽度变化)
- 适用场景:简单属性变化(如透明度渐变、位置移动)
- 关键属性:
html
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:1"/>
2、关键帧动画
- 原理:定义多个关键时间点的属性值,支持多种插值方式(线性、离散、样条)
- 示例:实现中间停顿的移动效果
html
<DoubleAnimationUsingKeyFrames>
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/>
<EasingDoubleKeyFrame Value="42.5" KeyTime="0:0:1" EasingFunction="{StaticResource CubicEase}"/>
<DiscreteDoubleKeyFrame Value="42.5" KeyTime="0:0:2"/>
<EasingDoubleKeyFrame Value="85" KeyTime="0:0:3"/>
</DoubleAnimationUsingKeyFrames>
3.关键帧类型:
html
<LinearDoubleKeyFrame Value="100" KeyTime="0:0:1" /> <!-- 线性过渡 -->
<DiscreteDoubleKeyFrame Value="200" KeyTime="0:0:2" /> <!-- 直接跳变 -->
<SplineDoubleKeyFrame Value="300" KeyTime="0:0:3"
KeySpline="0.5,0 0.5,1" /> <!-- 贝塞尔曲线控制速率 -->
3、路径动画
- 原理:元素沿 PathGeometry定义的路径运动(如 DoubleAnimationUsingPath)
- 适用场景:复杂轨迹运动(如曲线飞行)
html
<PathGeometry x:Key="MotionPath" Figures="M0,0 L100,100 C200,50 300,150 400,0"/>
<DoubleAnimationUsingPath Storyboard.TargetProperty="X"
PathGeometry="{StaticResource MotionPath}"/>
三、动画核心元素
1、动画类
- 继承自 Timeline,如ColorAnimation(颜色渐变)、ThicknessAnimation(边距动画)
- 命名规则:DataTypeAnimation(如DoubleAnimation)
2、故事板 (Storyboard)
- 作用:管理多个动画的播放顺序和同步
- 常用方法
cs
Storyboard.Begin(); // 开始动画
Storyboard.Stop(); // 停止
Storyboard.Pause(); // 暂停
Storyboard.Seek(TimeSpan); // 跳转到指定时间
3.代码控制:
cs
Storyboard sb = new Storyboard();
sb.Children.Add(animation1);
sb.Begin(element);
4.事件触发器 (EventTrigger)
XML
<Button Content="Click">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>...</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
5.数据触发器 (DataTrigger) / 属性触发器 (Trigger)
XML
<Style TargetType="Rectangle">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>...</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
3、依赖属性与接口
动画仅作用于依赖属性,且目标对象需实现 IAnimatable 接口(所有 UI 元素均支持)
四、缓动函数 (Easing Functions)
1、作用
改变动画速度曲线,实现自然过渡(如加速、弹跳)
2、常见类型
- 线性:LinearEase
- 物理模拟:BounceEase(弹跳)、ElasticEase(弹性)
- 自定义曲线:CubicEase(三次方缓动)
XML
<DoubleAnimation From="0" To="300" Duration="0:0:2">
<DoubleAnimation.EasingFunction>
<BounceEase Bounces="2" EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
五、动画实现方式
1、XAML 声明式
- 优点:直观易维护,适合简单动画
- 示例:按钮点击缩放
XML
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" To="200" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
2、C# 动态生成
- 适用场景:复杂逻辑控制(如根据用户输入生成动画)
- 代码示例:
cs
var animation = new DoubleAnimation
{
From = 0,
To = 100,
Duration = TimeSpan.FromSeconds(1),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
};
element.BeginAnimation(WidthProperty, animation);
六、高级动画技巧
1、动画组 (Animation Groups)
同时运行多个动画(如旋转 + 缩放)
XML
<Storyboard>
<DoubleAnimation TargetProperty="Width" To="150"/>
<DoubleAnimation TargetProperty="Height" To="150"/>
</Storyboard>
2、动画复用 (Resource + x:Key)
定义动画资源,全局复用:
XML
<Window.Resources>
<Storyboard x:Key="FadeInAnimation">
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0" To="1" Duration="0:0:1"/>
</Storyboard>
</Window.Resources>
3、组合动画 (Parallel vs Sequence)
- ParallelTimeline:并行执行多个动画。
- SequenceTimeline:按顺序执行动画。
4、路径动画优化
使用 PathGeometry的Simplify() 方法减少路径节点,提升性能
5、性能优化
1)启用硬件加速
- 设置 RenderOptions.BitmapScalingMode="HighQuality"
- 对复杂动画使用 CacheMode="BitmapCache"
2)减少实时渲染开销
- 优先使用 Opacity 替代 Visibility 做淡入淡出
- 避免频繁触发布局计算(如动画中修改 Width/Height)
- 避免高指数缓动函数(如 PowerEase 的 Power >5)
3)合理使用 FillBehavior
- FillBehavior="HoldEnd":动画结束后保持最终值(默认)
- FillBehavior="Stop":动画结束后恢复初始值
七、调试与常见问题
1. 动画不生效的排查步骤
- 确认目标属性是依赖属性(Dependency Property)
- 检查Storyboard.TargetName和TargetProperty是否正确
- 确保动画的 From/To 值在合理范围内
- 验证触发器是否被正确触发(如事件名称是否拼写错误)
2、其它
- 调试工具:WPF Performance Suite 分析动画帧率
- 设计工具:Blend for Visual Studio 可视化编辑动画路径
- 学习资源:微软官方动画指南.
八、示例
让圆点从左至右的动画效果 中间停顿 即先从左至中间 再从中间至右 的动画效果
XML
<Grid>
<Grid.Resources>
<Style x:Key="ellipse" TargetType="Ellipse">
<Setter Property="Width" Value="10" />
<Setter Property="Height" Value="10" />
<Setter Property="Canvas.Left" Value="0" />
<Setter Property="Fill" Value="Gray" />
<!--<Setter Property="RenderTransformOrigin" Value="0.5,3.33" />-->
</Style>
<PowerEase
x:Key="MidPauseEase"
EasingMode="EaseInOut"
Power="5" />
</Grid.Resources>
<Canvas Height="100">
<Canvas.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" Storyboard.TargetProperty="(Canvas.Left)">
<!-- e1 动画 -->
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="e1">
<EasingDoubleKeyFrame
EasingFunction="{StaticResource MidPauseEase}"
KeyTime="0:0:1.5"
Value="400" />
<EasingDoubleKeyFrame
EasingFunction="{StaticResource MidPauseEase}"
KeyTime="0:0:3"
Value="780" />
</DoubleAnimationUsingKeyFrames>
<!-- e2 动画 -->
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="e2">
<EasingDoubleKeyFrame
EasingFunction="{StaticResource MidPauseEase}"
KeyTime="0:0:1.7"
Value="388" />
<EasingDoubleKeyFrame
EasingFunction="{StaticResource MidPauseEase}"
KeyTime="0:0:3.2"
Value="780" />
</DoubleAnimationUsingKeyFrames>
<!-- e3 动画 -->
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="e3">
<EasingDoubleKeyFrame
EasingFunction="{StaticResource MidPauseEase}"
KeyTime="0:0:1.9"
Value="376" />
<EasingDoubleKeyFrame
EasingFunction="{StaticResource MidPauseEase}"
KeyTime="0:0:3.4"
Value="780" />
</DoubleAnimationUsingKeyFrames>
<!-- e4 动画 -->
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="e4">
<EasingDoubleKeyFrame
EasingFunction="{StaticResource MidPauseEase}"
KeyTime="0:0:2.1"
Value="364" />
<EasingDoubleKeyFrame
EasingFunction="{StaticResource MidPauseEase}"
KeyTime="0:0:3.6"
Value="780" />
</DoubleAnimationUsingKeyFrames>
<!-- e5动画 -->
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="e5">
<EasingDoubleKeyFrame
EasingFunction="{StaticResource MidPauseEase}"
KeyTime="0:0:2.3"
Value="352" />
<EasingDoubleKeyFrame
EasingFunction="{StaticResource MidPauseEase}"
KeyTime="0:0:3.8"
Value="780" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
<!-- 背景 -->
<Ellipse Width="100" Height="100" />
<Label
Width="100"
Height="100"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Content="Loading"
FontFamily="Times New Roman"
FontSize="16"
FontWeight="Bold"
Foreground="#6b48ff" />
<Ellipse Name="e1" Style="{StaticResource ellipse}" />
<Ellipse Name="e2" Style="{StaticResource ellipse}" />
<Ellipse Name="e3" Style="{StaticResource ellipse}" />
<Ellipse Name="e4" Style="{StaticResource ellipse}" />
<Ellipse Name="e5" Style="{StaticResource ellipse}" />
</Canvas>
</Grid>