热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Qt6.4中的新功能:帧动画

NewinQt6.4:FrameAnimationQt6.4中的新功能:帧动画August19,2022byKajGrnholm|Comments2022年8月
New in Qt 6.4: FrameAnimation
Qt 6.4中的新功能:帧动画

August 19, 2022 by Kaj Grönholm | Comments

2022年8月19日由Kaj Grönholm |评论

In this blog post we try to solve the classical "Mouse chasing Mouse" -problem. Don't know it? No problem, nobody does. But if you are interested in Qt Quick, smooth animations and what's new in Qt 6.4 (Beta3 was just released!), please continue reading and you'll find out!

在这篇博文中,我们试图解决经典的“老鼠追老鼠”问题。不知道吗?没问题,没人会。但如果您对Qt Quick、平滑的动画以及Qt 6.4(Beta3刚刚发布!)中的新功能感兴趣,请继续阅读,你会发现的!

When animating a Qt Quick property from A to B with speed X, you usually use some Animation element like PropertyAnimation, NumberAnimation or ColorAnimation. And for combining multiple animations, you can use ParallelAnimation or SequentialAnimation. These are declarative and work great in most cases. Sometimes in the middle of an animation the target changes to C instead (or back to A) and standard animations can handle also this case smoothly. But if C is a moving target, changing frequently or speed X should be adjustable during the animation (and not just some pre-defined easing curve), then these standard Qt Quick animations are not ideal anymore.

​当以速度X将Qt Quick属性从A设置为B时,通常使用一些动画元素,如PropertyAnimation、NumberAnimation或ColorAnimation。对于组合多个动画,可以使用ParallelAnimation或SequentialAnimation。这些是声明性的,在大多数情况下都非常有效。有时在动画中间,目标会改为C(或返回到A),标准动画也可以顺利处理这种情况。但是,如果C是一个移动目标,在动画期间频繁变化或速度X应该是可调整的(而不仅仅是一些预定义的缓和曲线),那么这些标准Qt Quick动画不再理想。

Now let's demonstrate this issue with a mouse chasing mouse tester application and present different (more or less optional) ways to solve it.

现在,让我们用一个鼠标追踪鼠标测试仪应用程序来演示这个问题,并给出不同的(或多或少是可选的)解决方法。

Solution 1: Behavior


解决方案1:行为

When I have property changes and need to animate them, first option that comes to my mind usually is adding a Behavior for those properties. So let's try that first. Attach the mouse position to mouseX and mouseY properties and whenever they change, instead of changing directly to the new values, use Behavior to animate the changes like this:

​当我有属性更改并需要设置动画时,我想到的第一个选项通常是为这些属性添加行为。让我们先试试。将鼠标位置附加到mouseX和mouseY属性,每当它们更改时,使用行为来动画化更改,而不是直接更改为新值:

property real mouseX: 0
property real mouseY: 0Behavior on mouseX {NumberAnimation {duration: 1000}
}
Behavior on mouseY {NumberAnimation {duration: 1000}
}

Here is what this solution looks like:

以下是此解决方案的外观:

https://youtu.be/3GdIUYVMwN8

There are a few issues with this approach: As the animations are duration-based, mouse moves faster when the distance is longer and slower when it is shorter. Another issue is that animations are recalculated & restarted whenever the mouse pointer moves, which causes extra CPU usage. This is especially notable when we artificially generate high CPU load → the animations become jumpy. Instead of NumberAnimation it would be possible to use SmoothedAnimation or SpringAnimation but the easings of those animation types don't really suit for this use case and they don't really fix the issues.

​这种方法存在一些问题:由于动画是基于持续时间的,所以当距离较长时,鼠标移动得更快,而当距离较短时,鼠标则移动得更慢。另一个问题是,每当鼠标指针移动时,动画都会重新计算和重新启动,这会导致额外的CPU使用。当我们人为地产生高CPU负载时,这一点尤为明显→ 动画变得跳跃。代替NumberAnimation,可以使用SmoothedAnimation或SpringAnimation,但这些动画类型的效果并不适合这个用例,也不能真正解决问题。

Solution 2: Timer


解决方案2:定时器

As the property animations seem a bit too restrictive for this use case, someone (not me) could consider using Timer with 16ms interval for 60fps animation action. Code for that would look something like this:

​由于属性动画对于这个用例来说似乎有点过于严格,所以有人(不是我)可以考虑使用16毫秒间隔的定时器来执行60帧的动画动作。代码如下所示:

Timer {running: truerepeat: trueinterval: 16onTriggered: {var xDelta = mouseArea.mouseX - mouseX;var yDelta = mouseArea.mouseY - mouseY;var length = Math.sqrt(xDelta * xDelta + yDelta * yDelta);var speed = 3.0;if (length > speed) {var xNormalized = xDelta / length;var yNormalized = yDelta / length;mouseX += xNormalized * speed;mouseY += yNormalized * speed;}}
}

And the outcome:

结果是:

https://youtu.be/kDCSs7WQfWY

Initially this Timer approach seems to work pretty well. But it also comes with its own flaws: As we set the interval to 16ms, the timer doesn't match well to non-60Hz animation refresh rates. Also, QML Timers are not really meant for animations and internally they have an extra Qt event loop roundtrip, meaning that they are not as tightly integrated with the animation loop. This is clearly visible when loading the event system e.g. by moving the window → the animation becomes jumpy. Lastly, if the target doesn't reach the intended fps for any reason, the mouse starts to move slower as the speed doesn't have any multiplier taking the animation frame rate into account. We could manually calculate some multiplier e.g. with Javascript Date & getMilliseconds() but that is an extra work we rather avoid if possible.

最初,这种计时器方法似乎工作得很好。但它也有自己的缺陷:当我们将时间间隔设置为16ms时,定时器与非60Hz动画刷新率不匹配。此外,QML定时器实际上并不适用于动画,在内部它们有一个额外的Qt事件循环往返,这意味着它们与动画循环没有紧密集成。这在加载事件系统时(例如通过移动窗口)清晰可见→ 动画变得跳跃。最后,如果目标由于任何原因没有达到预期的帧速率,则鼠标开始移动得较慢,因为速度没有考虑动画帧速率的任何乘数。我们可以手动计算一些乘数,例如使用Javascript Date&getmillizes(),但如果可能的话,我们宁愿避免额外的工作。

Solution 3: FrameAnimation


解决方案3:帧化

Next we will switch to the main topic of this blog post, the new FrameAnimation element. FrameAnimation can be considered as a "custom animation" where you control what happens each time it is triggered. Compared to Timer, FrameAnimation doesn't have repeat or interval properties, since the intervals are always synchronized with the animations and triggered once per Qt Quick animation frame. Source code of the FrameAnimation version is very similar to previous Timer code, with an addition to also rotate the mouse image:

​接下来,我们将切换到这篇博客文章的主要主题,新的FrameAnimation元素。FrameAnimation可以被视为“自定义动画”,您可以控制每次触发时发生的情况。与计时器相比,FrameAnimation不具有重复或间隔属性,因为间隔始终与动画同步,并且每Qt Quick animation帧触发一次。FrameAnimation版本的源代码与以前的定时器代码非常相似,除了旋转鼠标图像之外:

FrameAnimation {running: trueonTriggered: {var xDelta = mouseArea.mouseX - mouseX;var yDelta = mouseArea.mouseY - mouseY;var length = Math.sqrt(xDelta * xDelta + yDelta * yDelta);var speed = 3.0 * 60 * frameTime;if (length > speed) {var xNormalized = xDelta / length;var yNormalized = yDelta / length;mouseX += xNormalized * speed;mouseY += yNormalized * speed;var rot = Math.atan2(yDelta, xDelta) - (Math.PI / 2);mouseImage.rotation = rot * (180 / Math.PI);}}
}

And the outcome:

结果是:

https://youtu.be/paClowU51qw

This version is the one to use because FrameAnimation is better synchronized with the animation loop, getting triggered for every animation frame. This way it will adjust to different target screen refresh rates when using the threaded render loop with vsync-based throttling. You can also use frameTime (or smoothFrameTime) property as a multiplier and your animation speed will then adjust to different target fps, taking into account possible missed frames. Actually, FrameAnimation with smoothFrameTime property is used in this example also for the simple frame time & rate information, which is good enough for our needs to show when the animation speed drops:

​使用此版本是因为FrameAnimation与动画循环更好地同步,每个动画帧都会触发。这样,当使用基于vsync的线程渲染循环时,它将调整到不同的目标屏幕刷新率。您还可以使用frameTime(或smoothFrameTime)属性作为乘数,然后将动画速度调整到不同的目标fps,同时考虑可能丢失的帧。实际上,本示例中还使用了具有smoothFrameTime属性的FrameAnimation,用于简单的帧时间和速率信息,这足以满足我们在动画速度下降时显示的需要:

Text {text: fpsHelper.ft + " ms, " + fpsHelper.fps + " fps"FrameAnimation {id: fpsHelperreadonly property int fps: smoothFrameTime > 0 ? Math.round(1.0 / smoothFrameTime) : 0readonly property string ft: (1000 * smoothFrameTime).toFixed(1)running: true}
}

Performance considerations


性能考虑

Now you might think "OK, this FrameAnimation can do stuff but shouldn't we avoid QML Javascript code for optimal performance?". This was true in the past but not as much anymore. With Qt 6, a lot of work has been done to optimize the QML engine and the new Qt Quick Compiler even compiles QML Javascript to native C++ code. For more details, see the Qt Quick Compiler performance blog post and the related optimization series posts. So while you should still keep the frontend UI layer (QML) and the backend logic (C++) separate and have most of the imperative code in C++ side, don't be afraid of using QML scripts a bit more for UI related things.

​现在您可能会想“好吧,这个框架动画可以做一些事情,但我们不应该避免QML Javascript代码以获得最佳性能吗?”。这在过去是真实的,但现在不再如此。在Qt6中,已经做了大量工作来优化QML引擎,新的QtQuick编译器甚至将QML Javascript编译为本地C++代码。有关详细信息,请参阅Qt Quick编译器性能博客和相关优化系列文章。因此,虽然您仍然应该将前端UI层(QML)和后端逻辑(C++)分开,并将大部分命令代码放在C++端,但不要害怕将QML脚本更多地用于与UI相关的事情。

The example used in this blog post was just a quick tester app, but FrameAnimation can of course also be used to do much nicer things (which I will blog about soonish!). But in conclusion for this post: If you are using Qt 6.4 (or newer) and have a need for fully custom Qt Quick animations, consider using the new FrameAnimation element.

​这篇博文中使用的示例只是一个快速测试应用程序,但FrameAnimation当然也可以用来做更好的事情(我将在soonish的博客上讨论)。但在这篇文章的最后:如果您使用的是Qt 6.4(或更高版本),并且需要完全自定义的Qt Quick动画,请考虑使用新的FrameAnimation元素。


推荐阅读
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文详细解析了JavaScript中相称性推断的知识点,包括严厉相称和宽松相称的区别,以及范例转换的规则。针对不同类型的范例值,如差别范例值、统一类的原始范例值和统一类的复合范例值,都给出了具体的比较方法。对于宽松相称的情况,也解释了原始范例值和对象之间的比较规则。通过本文的学习,读者可以更好地理解JavaScript中相称性推断的概念和应用。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 无损压缩算法专题——LZSS算法实现
    本文介绍了基于无损压缩算法专题的LZSS算法实现。通过Python和C两种语言的代码实现了对任意文件的压缩和解压功能。详细介绍了LZSS算法的原理和实现过程,以及代码中的注释。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 关于CMS收集器的知识介绍和优缺点分析
    本文介绍了CMS收集器的概念、运行过程和优缺点,并解释了垃圾回收器的作用和实践。CMS收集器是一种基于标记-清除算法的垃圾回收器,适用于互联网站和B/S系统等对响应速度和停顿时间有较高要求的应用。同时,还提供了其他垃圾回收器的参考资料。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 网卡工作原理及网络知识分享
    本文介绍了网卡的工作原理,包括CSMA/CD、ARP欺骗等网络知识。网卡是负责整台计算机的网络通信,没有它,计算机将成为信息孤岛。文章通过一个对话的形式,生动形象地讲述了网卡的工作原理,并介绍了集线器Hub时代的网络构成。对于想学习网络知识的读者来说,本文是一篇不错的参考资料。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
author-avatar
changless
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有