动画框架
动画框架为 GUI 元素的动画制作提供了一种简便的方法。它能让您为 Qt Widgets 或QObject 的 Qt 属性值制作动画。该框架提供的大部分功能也可在 Qt Quick中也提供了该框架的大部分功能,在该框架中可以通过声明的方式定义动画。
本概述解释了该框架的架构,并通过示例演示了QObject 和 GUI 元素动画的常用技术。
动画架构
下图显示了框架提供的最重要的类:
它包括QAbstractAnimation 类,该类为动画提供了必要的基础。该类定义了框架支持的所有动画的通用属性。例如,启动、停止和暂停动画的功能。该类还能接收时间变化通知。
该框架进一步提供了QVariantAnimation 和QAnimationGroup 类,这两个类建立在其基例QAbstractAnimation 的基础上。层次结构中的下一个类是QPropertyAnimation ,它派生于QVariantAnimation ,可让您对 Qt Widgets 或QObject 的 Qt 属性制作动画。该类使用缓和曲线对属性值执行插值。有了这些,你只需要一个带有 Qt XML 属性值的QObject 类,就可以对其进行动画处理了。
复杂的动画可以通过建立QAbstractAnimations 的树状结构来构建,其中树状结构是一个包含其他动画的QAnimationGroup 。这些动画组还可以包含代表不同组或动画的子组,如QParallelAnimationGroup 和QSequentialAnimationGroup 。
在幕后,所有动画都由一个全局定时器控制,该定时器会将所有正在运行的动画发送到updates 。
有关这些单独类的详细信息及其在框架中的作用,请参阅它们的文档。
框架提供的类
这些类为创建简单和复杂的动画提供了必要的基础架构。
所有动画的基类 | |
动画组的抽象基类 | |
控制动画的缓和曲线 | |
平行动画组 | |
QSequentialAnimationGroup 的暂停 | |
动画 Qt 属性 | |
顺序动画组 | |
用于控制动画的时间轴 | |
动画基类 |
动画 Qt 属性
由于QPropertyAnimation 类可以对 Qt 属性进行插值,因此经常被使用。事实上,它的超类QVariantAnimation提供了updateCurrentValue() 的抽象实现,除非在valueChanged signal 上进行更改,否则不会改变任何值。
该框架可让您将 Qt 中现有类的 Qt 属性动画化。例如,可嵌入QGraphicsView的QWidget 类具有边界、颜色等属性。下面的示例演示了如何为QPushButton widget 制作动画:
#include <QApplication> #include <QPushButton> #include <QPropertyAnimation> class MyButtonWidget : public QWidget { public: MyButtonWidget(QWidget *parent = nullptr); }; MyButtonWidget::MyButtonWidget(QWidget *parent) : QWidget(parent) { QPushButton *button = new QPushButton(tr("Animated Button"), this); QPropertyAnimation *anim = new QPropertyAnimation(button, "pos", this); anim->setDuration(10000); anim->setStartValue(QPoint(0, 0)); anim->setEndValue(QPoint(100, 250)); anim->start(); } int main(int argc, char *argv[]) { QApplication a(argc, argv); MyButtonWidget buttonAnimWidget; buttonAnimWidget.resize(QSize(800, 600)); buttonAnimWidget.show(); return a.exec(); }
该示例对pos
Qt XML 属性进行了动画处理QPushButton ,使其在 10 秒(10000 毫秒)内从屏幕左上角移动到结束位置(250,250)。
它使用线性插值方法来控制起始值和结束值之间的动画速度。请尝试在起始值和结束值之间添加另一个值,看看它们是如何插值的。这次使用QPropertyAnimation::setKeyValueAt() 函数来添加这些值:
... anim->setDuration(10000); anim->setKeyValueAt(0, QPoint(0, 0)); anim->setKeyValueAt(0.8, QPoint(250, 250)); anim->setKeyValueAt(1, QPoint(0, 0)); ...
在本示例中,动画在 8 秒内将按钮移动到 (250, 250),并在剩余的 2 秒内将其移回原位。按钮的移动是在这些点之间线性插值的。
如果QObject 的值没有声明为 Qt XML 属性,而该值又有一个设置方法,那么您也可以将该值制成动画。在这种情况下,从包含该值的类派生一个新类,并为该值添加一个带有设置器的 Qt 属性。
注意: 每个 Qt 属性都需要一个 getter,因此如果没有定义 getter,则应提供一个 getter。
class MyGraphicsRectItem : public QObject, public QGraphicsRectItem { Q_OBJECT Q_PROPERTY(QPointF pos READ pos WRITE setPos) };
在本例中,MyGraphicsRectItem
派生于QGraphicsRectItem 和QObject ,并定义了pos
属性。即使QGraphicsRectItem 没有提供pos
属性,您也可以将项目的pos
动画化。
有关 Qt 属性系统的一般介绍,请参阅Qt 的属性系统。
动画和图形视图框架
QPropertyAnimation 也可用于对 进行动画处理,该 并不继承 。在这种情况下,您可以从要动画的图形项目派生出一个类。该派生类也应继承 的形式,以便在 上使用 。下面的示例展示了如何做到这一点:QGraphicsItem QObject QObject QGraphicsItem QPropertyAnimation
class Pixmap : public QObject, public QGraphicsPixmapItem { Q_OBJECT Q_PROPERTY(QPointF pos READ pos WRITE setPos) ... }
注: 您也可以派生自QGraphicsWidget ,它已经是一个QObject 。
如上一节所述,您需要定义要制作动画的属性。派生类必须首先从QObject 继承,因为元对象系统要求这样做。
缓和曲线
QPropertyAnimation 在开始和结束属性值之间执行线性插值。除了为动画添加更多键值外,还可以选择一条缓和曲线,在不改变路径的情况下,控制 0 和 1 之间的插值速度。
MyButtonWidget::MyButtonWidget(QWidget *parent) : QWidget(parent) { QPushButton *button = new QPushButton(tr("Animated Button"), this); QPropertyAnimation *anim = new QPropertyAnimation(button, "pos", this); anim->setDuration(10000); anim->setStartValue(QPoint(0, 0)); anim->setEndValue(QPoint(100, 250)); anim->setEasingCurve(QEasingCurve::OutBounce); anim->start(); }
在本例中,动画遵循一条曲线,使button
像球一样弹跳。QEasingCurve 提供了大量曲线,可从QEasingCurve::Type 枚举中选择。如果您想使用其他不可用的曲线,请自行实现并在QEasingCurve 注册。
动画分组
一个应用程序通常包含多个动画。例如,应用程序希望同时移动多个图形项,或依次移动它们。
QAnimationGroup的子类(QSequentialAnimationGroup 和QParallelAnimationGroup)是其他动画的容器,因此这些动画可以按顺序或并行动画化。QAnimationGroup 不会对属性进行动画处理,但会定期收到时间变化的通知。这样,它就能将这些时间变化转发给动画组,从而控制动画的播放时间。
下面两个示例演示了QSequentialAnimationGroup 和QParallelAnimationGroup 的使用:
MyButtonWidget::MyButtonWidget(QWidget *parent) : QWidget(parent) { QPushButton *bonnie = new QPushButton(tr("Bonnie"), this); QPushButton *clyde = new QPushButton(tr("Clyde"), this); QPropertyAnimation *anim1 = new QPropertyAnimation(bonnie, "pos", this); anim1->setDuration(3000); anim1->setStartValue(QPoint(0, 0)); anim1->setEndValue(QPoint(100, 250)); QPropertyAnimation *anim2 = new QPropertyAnimation(clyde, "pos", this); anim2->setDuration(3000); anim2->setStartValue(QPoint(100, 250)); anim2->setEndValue(QPoint(500, 500)); QParallelAnimationGroup *parallelAnim = new QParallelAnimationGroup; parallelAnim->addAnimation(anim1); parallelAnim->addAnimation(anim2); parallelAnim->start(); }
并行动画组同时播放多个动画。它的start() 函数会启动属于该组的所有动画。
MyButtonWidget::MyButtonWidget(QWidget *parent) : QWidget(parent) { QPushButton *bonnie = new QPushButton(tr("Bonnie"), this); QPushButton *clyde = new QPushButton(tr("Clyde"), this); QPropertyAnimation *anim1 = new QPropertyAnimation(bonnie, "pos", this); anim1->setDuration(3000); anim1->setStartValue(QPoint(0, 0)); anim1->setEndValue(QPoint(100, 250)); QPropertyAnimation *anim2 = new QPropertyAnimation(clyde, "pos", this); anim2->setDuration(3000); anim2->setStartValue(QPoint(0, 0)); anim2->setEndValue(QPoint(200, 250)); QSequentialAnimationGroup *sequenceAnim = new QSequentialAnimationGroup; sequenceAnim->addAnimation(anim1); sequenceAnim->addAnimation(anim2); sequenceAnim->start(); }
顾名思义,QSequentialAnimationGroup 按顺序播放动画。前一个动画播放完毕后,它将启动列表中的下一个动画。
一个组本身就是一个动画,因此你可以将它添加到另一个组中。这样就可以建立一个动画树,定义动画的播放时间。
对象所有权
QPropertyAnimation 应始终有一个控制其生命周期的父对象。一个典型的应用程序可能包含多个动画组,动画组拥有这些动画的所有权。独立的QPropertyAnimation 必须明确指定一个父对象来控制其生命周期。在下面的示例中,可以看到独立的QPropertyAnimation 将QApplication 实例作为其父对象:
#include <QApplication> #include <QPushButton> #include <QPropertyAnimation> class MyButtonWidget : public QWidget { public: MyButtonWidget(QWidget *parent = nullptr); }; MyButtonWidget::MyButtonWidget(QWidget *parent) : QWidget(parent) { QPushButton *button = new QPushButton(tr("Animated Button"), this); QPropertyAnimation *anim = new QPropertyAnimation(button, "pos", this); anim->setDuration(10000); anim->setStartValue(QPoint(0, 0)); anim->setEndValue(QPoint(100, 250)); anim->start(); } int main(int argc, char *argv[]) { QApplication a(argc, argv); MyButtonWidget buttonAnimWidget; buttonAnimWidget.resize(QSize(800, 600)); buttonAnimWidget.show(); return a.exec(); }
注: 您还可以在启动动画时选择delete policy 来控制动画的生命周期。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.