WPF 机械类组件动画制作流程简述
一、创建组件
创建组件用户控件,将组件的各部分 “零件”(图片) 拼装在一起,形成组件的默认状态:

二、给运动部分加上 RenderTransform
需要平移则加上 TranslateTransform,需要旋转则加上 RotateTransform,并起好名称:

可改变一下数值看看效果:

三、添加转换动画(视觉状态)
在组件布局代码的外面放置 VisualStateManager.VisualStateGroups,一个 VisualStateGroup 代表一组互斥的视觉状态。此处定义了三个视觉状态(Normal、LeftExtend、RightExtend),共同组成了 “手臂状态” 这个视觉状态组:

三个视觉状态分别设置了对应变换的 X 值:

示例代码:
<!-- 转换动画 --> <VisualStateGroup Name="ArmState"> <VisualState Name="Normal"> <Storyboard FillBehavior="HoldEnd" SpeedRatio="1"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="topTranslateTransform" Storyboard.TargetProperty="X"> <LinearDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState Name="LeftExtend"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="topTranslateTransform" Storyboard.TargetProperty="X"> <LinearDoubleKeyFrame KeyTime="0:0:0.5" Value="-30"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState Name="RightExtend"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="topTranslateTransform" Storyboard.TargetProperty="X"> <LinearDoubleKeyFrame KeyTime="0:0:0.5" Value="30"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup>
20250514 更新:动画简化
由于 VisualStateGroup 可不指定名称,Storyboard 的 FillBehavior 默认值就是 HoldEnd,SpeedRatio 默认值就是 1,简单的情况下 DoubleAnimationUsingKeyFrames 可换为 DoubleAnimation,所以视觉状态组可简化为如下所示:

文字版:
<VisualStateGroup>
<VisualState Name="OpenUp">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rgdTranslate" Storyboard.TargetProperty="X" To="211" Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="rgdTranslate" Storyboard.TargetProperty="Y" To="33" Duration="0:0:1"/>
</Storyboard>
</VisualState>
<VisualState Name="OpenDown">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rgdTranslate" Storyboard.TargetProperty="X" To="211" Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="rgdTranslate" Storyboard.TargetProperty="Y" To="63" Duration="0:0:1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
四、使用视觉状态
首先定义一个相应的视觉状态枚举,Key 和之前定义的视觉状态名称相同,方便之后使用:

然后在用户控件后台代码中新增一个依赖属性,用于给外界绑定相应的视觉状态,并在变动方法中使用 VisualStateManager.GoToState 方法进行状态的切换,其中一个视觉状态名称的参数就使用了创建的枚举的字符串形式:

同时重写 OnApplyTemplate () 方法,在其中将相关视觉状态切换为初始值:

注意:之前定义 VisualStateGroup 时的名称 “ArmState”,可能会和依赖属性 ArmState 冲突,建议两者不同名。这里我将依赖属性重命名为 FtrArmState。
20250310 更新:
VisualStateGroup 可不加名称:
五、外部使用
首先给新建的用户控件创建一个对应的 ViewModel 以便使用,在其中添加一个绑定属性,以及一个测试用的命令:

将这个 VM 在要使用的界面的 VM 中进行声明和创建,然后在前台绑定即可:

效果(动图):

这个就是控件内部的动画,如果还需要控件整体的平移等动画,可以将其当作一个零件,然后在外部再按照本文的方法制作动画,原理是一样的。
20250305 更新:外部绑定 DataContext
用户控件的 Xaml 中通过样式来设置相关状态的绑定:

使用之处给 DataContext 绑定 ViewModel 即可:

20250310 更新:外部整体指定动画注意事项
直接给用户控件指定变换会报错:

需要再包一层来指定变换:

20250514 更新:动画部分直接放在页面中
如果动画部分没有放在单独的用户控件中,而是直接放在某个页面(当然可能也是一个用户控件)中:

也就是说,它用的依赖属性和绑定的值都是属于页面的(后台和VM中),那么 VisualStateManager.GoToState 也会返回 false,此时可换用 VisualStateManager.GoToElementState 方法,并将第一个参数指定为 包含 VisualStateManager 的根元素(如上图中的 visualStateRoot):

也就是说,GoToState 方法适用于从控件内部切换状态,GoToElementState 方法适用于从控件外部切换状态(参考:微软文档)。
六、关于 VisualStateGroups 的放置位置
以下为正确位置:


如果放在图 1 的 “机械运动区” 的 Canvas 中,则 VisualStateManager.GoToState 方法会返回 false。
下面的放置方式也是不行的:

也就是说控件的布局代码和视觉状态组需要并列地放在同一个面板 (UserControl 不行) 中。
原创文章,转载请注明: 转载自 独立观察员(dlgcy.com)
本文链接地址: [WPF 机械类组件动画制作流程简述](https://dlgcy.com/wpf-create-machine-animation-simple-steps/)
关注微信公众号 独立观察员博客(DLGCY_BLOG) 第一时间获取最新文章

2条评论