Avalonia 已知问题 第二次 Composition Animation 无法播放

news/2025/1/22 18:10:14/文章来源:https://www.cnblogs.com/lindexi/p/18512022

这是 Avalonia 的已知问题,我已经报告给官方,详细请看 https://github.com/AvaloniaUI/Avalonia/pull/17370

我尝试修复了此问题,请看 https://github.com/AvaloniaUI/Avalonia/pull/17370

复现的步骤如下:

  1. 在界面放入一个 UI 控件,如 Border 控件
  2. 通过 ElementComposition.GetElementVisual 方法获取 CompositionVisual 对象,再使用此对象创建和播放一个 Vector3DKeyFrameAnimation 动画
  3. 重复执行步骤 2

此时你可以看到重复执行步骤 2 时,原本正在播放的动画已经停止播放了

以下是我的 XAML 界面代码

    <Grid><Border x:Name="ScanBorder" ZIndex="101" IsVisible="True" HorizontalAlignment="Center" VerticalAlignment="Top"Height="220" Width="600"><Border.Background><LinearGradientBrush StartPoint="0%,0%" EndPoint="0%,100%"><GradientStop Color="#0033CEFF" Offset="0" /><GradientStop Color="#CC3592FF" Offset="1" /></LinearGradientBrush></Border.Background><Border.RenderTransform><TranslateTransform /></Border.RenderTransform></Border><Button x:Name="ControlButton" Content="Click" Click="ControlButton_OnClick"></Button></Grid>

以下是我的 C# 代码

    private Vector3DKeyFrameAnimation? _vector3DKeyFrameAnimation;private CompositionVisual? _scanBorderCompositionVisual;private void ControlButton_OnClick(object? sender, RoutedEventArgs e){_scanBorderCompositionVisual = ElementComposition.GetElementVisual(ScanBorder)!;var compositor = _scanBorderCompositionVisual.Compositor;_vector3DKeyFrameAnimation = compositor.CreateVector3DKeyFrameAnimation();_vector3DKeyFrameAnimation.InsertKeyFrame(0f, _scanBorderCompositionVisual.Offset with { Y = 0 });_vector3DKeyFrameAnimation.InsertKeyFrame(1f, _scanBorderCompositionVisual.Offset with { Y = this.Bounds.Height - ScanBorder.Height });_vector3DKeyFrameAnimation.Duration = TimeSpan.FromSeconds(2);_vector3DKeyFrameAnimation.IterationBehavior = AnimationIterationBehavior.Count;_vector3DKeyFrameAnimation.IterationCount = 30;_scanBorderCompositionVisual.StartAnimation("Offset", _vector3DKeyFrameAnimation);}

我将最简复现步骤的例子项目上传到 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快

先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin f82af28bab6f5cdfbd13c48c19b4f0a21a50ae06

以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin f82af28bab6f5cdfbd13c48c19b4f0a21a50ae06

获取代码之后,进入 AvaloniaIDemo/JallkeleejurCihayaiqalker 文件夹,即可获取到源代码

预期的行为是能够控制 Composition 的 Animation 动画的停止以及开启新的动画

根据我的分析问题原因是在更基础的 InlineDictionary 在处理单项重新赋值时的不正确行为,让动画模块第二次进入时不能符合预期工作

根据阅读 Avalonia 的代码可以看到 InlineDictionary 在只有单项的行为是通过 Set 方法调用进入时,将会忽略 overwrite 参数,从而导致 InlineDictionary 只有一项时,再次调用 Set 时的效果将会和调用 Add 方法相同。此行为将导致 composition animation 动画播放行为不符合预期,将导致第二次的 composition animation 无法播放。为什么第二次的 composition animation 无法播放?原因是第二次准备播放的 composition animation 无法将第一次的 composition animation 替换掉,而是将第二次的 composition animation 加入到第一次的 composition animation 后面,从而导致第二次设置的 composition animation 无法被执行

核心代码如下

internal struct InlineDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>> where TKey : class
{public void Set(TKey key, TValue value) => SetCore(key, value, true);public TValue this[TKey key]{get{if (TryGetValue(key, out var rv))return rv;throw new KeyNotFoundException();}set => Set(key, value);}void SetCore(TKey key, TValue value, bool overwrite){if (key == null)throw new ArgumentNullException();if (_data == null){_data = key;_value = value;} else if (_data is KeyValuePair[] arr){...}else if (_data is Dictionary<TKey, TValue?> dic){...}else{// We have a single element, upgrade to array.arr = new KeyValuePair[6];arr[0] = new KeyValuePair((TKey)_data, _value);arr[1] = new KeyValuePair(key, value);_data = arr;_value = default;}}
}

通过以上代码分析可以看到,在 InlineDictionary 首次加入时,将会进入 if (_data == null) 分支,使用如下代码分别给 _data_value 赋值

但是第二次进来的时候,将会进入 else 分支,在这个分支里面啥都判断,没有判断 overwritekey 的值,直接就创建为 KeyValuePair 数组。这就意味着第二次进入的时候,将让 Set 方法和 Add 方法相同,都是做添加而不是替换

这就导致了在 Composition 的 Animation 动画里面第二次设置动画的时候,停止播放动画

如以下的 ServerObjectAnimations 代码,可以看到在加入动画的时候,先获取旧的代码,将其调用 Deactivate 停下,再将其赋值为新的动画

class ServerObjectAnimations
{... // 忽略其他代码private InlineDictionary<CompositionProperty, ServerObjectAnimationInstance> _animations;public void OnSetAnimatedValue<T>(CompositionProperty<T> prop, ref T field, TimeSpan committedAt, IAnimationInstance animation) where T : struct{if (_owner.IsActive && _animations.TryGetValue(prop, out var oldAnimation))oldAnimation.Animation.Deactivate();_animations[prop] = new ServerObjectAnimationInstance<T>(this, animation, prop);animation.Initialize(committedAt, ExpressionVariant.Create(field), prop);if(_owner.IsActive)animation.Activate();OnSetDirectValue(prop);}
}

由于 InlineDictionary 存在问题,只有一项的时候,赋值进入第二项,做的是添加第二项但不删除第一项。这就导致第二次加入动画时候,第一个动画被停止,但是第一个动画还在字典里面,后续获取将会返回第一个动画。第二个动画将不会被返回。这就是为什么第二次的动画无法播放的原因

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/823513.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

读数据工程之道:设计和构建健壮的数据系统23批量获取的考虑因素

批量获取的考虑因素1. 批量获取的考虑因素 1.1. 批量获取,通常是获取数据的一种便捷方式1.1.1. 通过从源系统中抽取一个数据子集,根据时间间隔或累积数据的大小来获取数据1.2. 基于时间间隔的批量获取在传统ETL的数据仓库中很普遍1.2.1. 每天在非工作时间(也可以按其他频率)…

分段任意模型满足零样本6D对象姿态估计

6.11 SAM-6D:分段任意模型满足零样本6D对象姿态估计6.11.1 SAM-6D:分段任意模型满足零样本6D对象姿态估计概述零样本6D物体姿态估计涉及在杂乱场景中检测具有6D姿态的新物体,这对模型的可推广性提出了重大挑战。幸运的是,最近的Segment Anything Model(SAM)展示了非凡的零…

Educational Codeforces Round 171 div2(A~E)

Educational Codeforces Round 171 div2(A~E) Dashboard - Educational Codeforces Round 171 (Rated for Div. 2) - Codeforces 火车头 #define _CRT_SECURE_NO_WARNINGS 1#include <algorithm> #include <array> #include <bitset> #include <cassert&g…

基于STM32cubeMX 的 RT-Thread 的默认串口输出修改及内容修改

最近在学习RT-Thread, 随便做点小笔记, 方便焦虑小白找路 本随笔主要写两个问题, 1.默认UART2修改成其他UART; 2. 开机输出的内容修改 在RT-Thread 中 默认的系统输出串口是UART2; 如果想修改成其他串口(以UART1为例子), 分两步走, 1.1. 初始化 对应的串口 (也就是UART1) 1.2…

qt的无边框窗口支持拖拽、Aero Snap、窗口阴影等特性

qt的无边框窗口支持拖拽、Aero Snap、窗口阴影等特性环境:Desktop Qt 5.4.1 MSVC2013 32bit 需要的库:dwmapi.lib、user32.lib 需要头文件:<dwmapi.h>、<windowsx.h>只显示重要代码1、去除原边框、加上阴影、Aero Snap以及其他动画特效 (1)头文件 #include &q…

Spring 框架:Java 开发者的春天

Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。一、关于Spring 1.1 简介 Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称…

Mooc下载器:中国大学mooc慕课视频课件课程下载工具,如何在电脑端下载中国大学mooc慕课视频课程课件资料到本地?

一. 安装中国大学mooc慕课课程下载器 1.获取学无止下载器 https://www.xuewuzhi.cn/icourse163_downloader 2.下载安装后,然后点击桌面快捷方式运行即可。 注意:杀毒软件可能会阻止外部exe文件运行,并将其当做成病毒,直接添加信任即可,本软件绝对没有木马病毒。 二. 使用…

千聊兴趣岛视频课件课程下载工具,如何在电脑端下载千聊兴趣岛视频课程课件到本地?

一. 安装千聊/兴趣岛课程下载器 1.获取学无止下载器 https://www.xuewuzhi.cn/qlchat_downloader 2.下载安装后,然后点击桌面快捷方式运行即可。 注意:杀毒软件可能会阻止外部exe文件运行,并将其当做成病毒,直接添加信任即可,本软件绝对没有木马病毒。 二. 使用说明 1.学无…

T241029

有限可加性和次可列可加性结合等价于可列可加性

一文搞懂会计科目设计原理及运维要点

在企业的财务管理中,会计科目的设计和维护是核心环节,它不仅关系到财务数据的准确性,还直接影响到企业的决策和合规性。这篇文章,我们一起来学习一下相关知识。会计科目是业务事项按会计准则要求的一种归纳或分类,是在会计要素基础上的一种细分,包括科目编码和科目名称;…

Ubuntu系统配置Zabbix前端

1.zabbix 登录1.访问zabbix的前端地址(10.0.0.6/zabbix/setup.php)进入欢迎界面点击下一步即可2.如下图所示,下拉进度条进行条件检查,如果所有的参数都是"OK",则可以进入下一步3.配置zabbix前端的数据库信息根据数据库信息填写即可4 .配置zabbix server信息5.将之前…

Blender 骨骼绑定记录

首先先从Free3D网站下载一个免费的Blender模型,将它导入Blender需要检查:模型原点是否在世界坐标原点 Location,Rotation,Scale是否归一化3D游标是否在世界坐标原点,不在可以按Shift+C在正视图下(1)创建根骨架修改骨架视图显示改到侧视图,并将它置平因为它不绑定权重,所…