当前位置: 首页 > news >正文

Avalonia中的动画

义动画示例Avalonia的官方示例项目(RenderDemo https://github.com/AvaloniaUI/Avalonia/tree/master/samples/RenderDemo)中,实现了一个名为CustomStringAnimator(https://github.com/AvaloniaUI/Avalonia/blob/master/samples/RenderDemo/Pages/CustomStringAnimator.cs)的动画类,它继承自InterpolatingAnimator<string>,并重写了Interpolate方法来实现打字机效果的动画。以下是该动画类的代码:

自定义动画


使用起来跟普通的动画几乎一样,需要指定Animator,代码如下(https://github.com/AvaloniaUI/Avalonia/blob/master/samples/RenderDemo/Pages/CustomAnimatorPage.xaml):

View Code

值得注意的是在axaml中使用自定义动画器时,需要指定动画器的类型(<Animation.Animator><pages:CustomStringAnimator/></Animation.Animator>),否则会报错。改进的字符串动画在官方示例的基础上,我们可以略微做一些改进:支持在字符串之间进行插值动画。动画过程中会根据进度逐渐显示新字符串的内容,同时保留旧字符串的公共前缀部分。

public class StringAnimator : InterpolatingAnimator<string> { /// <summary> /// 计算插值结果。 /// </summary> /// <param name="progress">进度:0到1之间的值,指在两个关键帧之间过渡的时间位置。</param> /// <param name="oldValue">旧值:动画开始时,第一个关键帧的值。</param> /// <param name="newValue">新值:动画结束时,最后一个关键帧的值。</param> /// <returns>返回插值结果。</returns> public override string Interpolate(double progress, string oldValue, string newValue) { if (progress <= 0) return oldValue; if (progress >= 1) return newValue; int oldLength = string.IsNullOrEmpty(oldValue) ? 0 : oldValue.Length; int newLength = string.IsNullOrEmpty(newValue) ? 0 : newValue.Length; if (oldLength == 0 && newLength == 0) return string.Empty; if (oldLength == newLength && oldValue == newValue) return newValue; if (oldLength == 0 || newValue.StartsWith(oldValue)) { // 如果旧值为空或新值以旧值开头,则从旧值开始插入新值的剩余部分 return newValue.Substring(0, (int)(oldLength + (newLength - oldLength) * progress)); } else if (newLength == 0 || oldValue.StartsWith(newValue)) { // 如果新值为空或旧值以新值开头,则从新值开始删除旧值的剩余部分 return oldValue.Substring(0, (int)(newLength + (oldLength - newLength) * (1 - progress))); } else { //如果没有包含关系,忽略旧值,直接从新值开始插入 oldLength = 0; return newValue.Substring(0, (int)(oldLength + (newLength - oldLength) * progress)); } } static StringAnimator() { // 注册动画器 Animation.RegisterCustomAnimator<string, StringAnimator>(); } }

使用方法跟官方示例一样。自定义离散动画如果要实现通用的离散动画,该怎么实现呢?跟上面类似,我们可以从InterpolatingAnimator<T>继承一个DiscreteAnimator<T>类,代码很简单,放弃中间的过渡部分,直接在进度达到100%时切换到新值,代码如下:

public class DiscreteAnimator<T> : InterpolatingAnimator<T> { public override T Interpolate(double progress, T oldValue, T newValue) { return progress < 1 ? oldValue : newValue; } static DiscreteAnimator() { // 注册动画器 Animation.RegisterCustomAnimator<T, DiscreteAnimator<T>>(); } }

使用方法跟上面一样:

<TextBlock Text="离散动画器文本,切换几种不同的样式。"> <TextBlock.Styles> <Style Selector="TextBlock"> <Style.Animations> <Animation Duration="0:0:3" IterationCount="Infinite" PlaybackDirection="Alternate"> <KeyFrame Cue="0%"> <Setter Property="FontStyle" Value="Normal"> <Animation.Animator> <app:DiscreteAnimator x:TypeArguments="FontStyle" /> </Animation.Animator> </Setter> </KeyFrame> <KeyFrame Cue="30%"> <Setter Property="FontStyle" Value="Italic"> <Animation.Animator> <app:DiscreteAnimator x:TypeArguments="FontStyle" /> </Animation.Animator> </Setter> </KeyFrame> <KeyFrame Cue="60%"> <Setter Property="FontStyle" Value="Oblique"> <Animation.Animator> <app:DiscreteAnimator x:TypeArguments="FontStyle" /> </Animation.Animator> </Setter> </KeyFrame> </Animation> </Style.Animations> </Style> </TextBlock.Styles> </TextBlock>

因为我们使用了泛型,在指定动画器时需要使用x:TypeArguments来指定类型参数(<app:DiscreteAnimator x:TypeArguments="FontStyle" />),否则会报错。另一种实现自定义动画的变通方法如果不想实现通用的动画器,我们可以注册AvaloniaProperty,并在属性的PropertyChanged回调中直接修改属性值来实现动画效果。以下是一个简单的示例,演示如何通过注册一个名为DiscreteValue的AvaloniaProperty来实现几何路径变换的动画:

//注册一个AvaloniaProperty,类型为int,默认值为0 public const string DISCRETE_VALUE = "DiscreteValue"; public static readonly StyledProperty<int> DiscreteValueProperty = AvaloniaProperty.Register<Path, int>(DISCRETE_VALUE, 0); //定义一个数组,存储不同的几何路径,在动画过程中根据DiscreteValue的值来切换不同的路径 private PathGeometry[] bells = []; //这里省略了路径的定义 //创建一个动画并运行它 private void PlayAnimation_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var animation = new Animation() { Duration = TimeSpan.FromSeconds(8), IterationCount = new IterationCount(2), PlaybackDirection = PlaybackDirection.Alternate, Children = { new KeyFrame() { Setters = { new Setter(DiscreteValueProperty, 0), }, KeyTime = TimeSpan.FromSeconds(0) }, new KeyFrame() { Setters = { new Setter(DiscreteValueProperty, 1), }, KeyTime = TimeSpan.FromSeconds(2) }, new KeyFrame() { Setters = { new Setter(DiscreteValueProperty, 2), }, KeyTime = TimeSpan.FromSeconds(4) }, new KeyFrame() { Setters = { new Setter(DiscreteValueProperty, 3), }, KeyTime = TimeSpan.FromSeconds(6) }, new KeyFrame() { Setters = { new Setter(DiscreteValueProperty, 3), }, KeyTime = TimeSpan.FromSeconds(8) } } }; _ = animation.RunAsync(Path1); } //在属性的PropertyChanged事件中,根据DiscreteValue的值来切换不同的路径 private void Path1_PropertyChanged(object? sender, Avalonia.AvaloniaPropertyChangedEventArgs e) { if (e.Property.Name == DiscreteValueProperty.Name) { Debug.WriteLine($"time: {DateTime.Now}, Path1 property changed: {e.Property.Name}, old value: {e.OldValue}, new value: {e.NewValue}"); int value = (int)e.NewValue; Path1.Data = bells[value % bells.Length]; } }
http://www.jsqmd.com/news/1092552/

相关文章:

  • 基于FPGA实现LVDS_7to1接口显示屏显示
  • Google限制Meta使用Gemini模型 凸显AI授权竞争白热化
  • openEuler双桌面环境实战:从ukui到dde的安装与多模式切换指南
  • 报社登报声明一般多少钱?办理登报声明的流程怎么走?
  • BiliTools:一款让你高效管理B站资源的跨平台工具箱
  • NoFences:你的Windows桌面需要一场空间革命吗?
  • 2026 年全球首个自动驾驶法规获批,终结标准割裂,中国深度参与重塑产业格局
  • 【计算机毕业设计案例】基于 Spring Boot+Vue 的电影售票系统设计与实现 前后端分离架构下影院在线购票管理平台(程序+文档+讲解+定制)
  • 抓“静电”痕:ESD失效分析技术实战
  • 到底 TMD 用哪个: npm, pnpm, Yarn, Bun, Deno? 傻瓜, 当然用 npm 啦
  • 为什么需要一个“闭环“
  • 2026年数据安全评估师认证:五位一体技术体系深度解析
  • 如何用BiliTools轻松管理你的B站数字资产?3大核心功能深度解析
  • 使用第三方 API 时保留 Codex 远程操作和官方插件:CC Switch 配置攻略
  • 从零搭建HTTPS双向认证:Nginx+Spring Boot实战与证书管理
  • C# 与 OpenTK:从入门到实战,构建你的第一个3D图形应用
  • 3大实用场景+40+模板:Dify工作流宝库让AI应用开发像搭积木一样简单
  • 当 Alpha 开始影响价格:SEER 如何用符号回归拆出非线性市场冲击
  • PyTorch 实战联邦学习FedAvg:从零构建到隐私保护模型聚合
  • 如何高效管理演示时间:智能PPT计时器的完整指南
  • Git 快速上手指南:半小时掌握日常开发必备命令
  • RSA非对称加密在登录模块的实战应用:从原理到前后端完整实现
  • H3C IPv6实战:从手工配置到无状态自动获取
  • 如何在Windows上为所有游戏添加Steam控制器全局支持?GlosSI完整指南
  • Caffeine是否为分布式缓存
  • nlohmann/json:现代C++ JSON处理的终极完整指南
  • 如何下载Java 26 的下载入口:
  • LitCAD:C开发的免费开源二维CAD软件完整入门指南
  • 破解Unity手游黑盒:Il2CppDumper如何让IL2CPP逆向分析不再神秘
  • WorkshopDL:终极Steam创意工坊下载器 - 轻松获取海量游戏模组