【翻译】Qt动画框架

动画框架旨在为创建动画和流畅的 GUI 提供一种简单的方法。通过动画 Qt 属性,该框架为动画小部件和其他 QObject 提供了很大的自由。该框架还可以与图形视图框架一起使用。动画框架中可用的许多概念在 Qt Quick 中也可用,它提供了一种定义动画的声明方式。有关动画框架的大部分知识都可以应用于 Qt Quick。

一、动画架构

下图显示了动画框架中最重要的类。

QAbstractAnimation是所有动画的祖先。它代表框架中所有动画通用的基本属性,尤其是启动、停止和暂停动画的能力。它还接收时间更改通知。

动画框架进一步提供了 QPropertyAnimation类,该类继承 QVariantAnimation并执行 Qt 属性的动画(属性是 Qt 元对象系统的一部分)。该类使用缓动曲线对属性执行插值。所以当想为一个值设置动画时,可以将它声明为一个属性并使该类成为 QObject的子类,这使得可以自由地为现有的小部件和其他 QObject 设置动画。

可以通过构建 QAbstractAnimations树结构来构建复杂的动画。该树是通过使用 QAnimationGroups构建的,QAnimationGroups 用作其他动画的容器。注意,组是 QAbstractAnimation 的子类,因此组本身可以包含其他组。

在幕后,动画由全局计时器控制,该计时器向所有正在播放的动画发送更新。

二、类列表

三、Qt 属性动画

QPropertyAnimation 类可以对 Qt 属性进行插值。

选择为 Qt 属性设置动画的一个主要原因是它可以自由地为 Qt API 中的现有类设置动画。看一个小例子:

QPushButton button("Animated Button");
button.show();

QPropertyAnimation animation(&button, "geometry");
animation.setDuration(10000);
animation.setStartValue(QRect(0, 0, 100, 30));
animation.setEndValue(QRect(250, 250, 100, 30));

animation.start();

此代码将在 10 秒(10000 毫秒)内将按钮从屏幕左上角移动到位置 (250, 250)。

上面的示例将在开始值和结束值之间进行线性插值。也可以设置位于开始值和结束值之间的值。 然后插值将通过这些点:

QPushButton button("Animated Button");
button.show();

QPropertyAnimation animation(&button, "geometry");
animation.setDuration(10000);

animation.setKeyValueAt(0, QRect(0, 0, 100, 30));
animation.setKeyValueAt(0.8, QRect(250, 250, 100, 30));
animation.setKeyValueAt(1, QRect(0, 0, 100, 30));

animation.start();

动画将在 8 秒内将按钮带到 (250, 250),然后在剩余的 2 秒内将其移回其原始位置。运动将在这些点之间线性插值。

还可以对未声明为 Qt 属性的 QObject 的值进行动画处理。唯一的要求是这个属性有一个 setter。 注意,每个 Qt 属性都需要一个 getter。如:

    Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry)

Qt 属性系统见:Qt属性系统

四、动画和图形视图框架

当想要为 QGraphicsItems 设置动画时,还可以使用 QPropertyAnimation。但是,QGraphicsItem不继承 QObject,需要改用 QGraphicsObjectQGraphicsWidget或同时继承 QObject 和QGraphicsItem。

class Pixmap : public QObject, public QGraphicsPixmapItem
{
    Q_OBJECT
    Q_PROPERTY(QPointF pos READ pos WRITE setPos)
    ...

注意,QObject 必须是第一个继承的类,因为元对象系统需要这样做。

五、缓和曲线

QPropertyAnimation在开始和结束属性值之间执行插值。除了向动画添加更多关键值之外,还可以使用缓和曲线。缓和曲线描述了一个函数,该函数控制 0 和 1 之间的插值速度应该如何,如果想在不更改插值路径的情况下控制动画的速度,则该曲线很有用。

QPushButton button("Animated Button");
button.show();

QPropertyAnimation animation(&button, "geometry");
animation.setDuration(3000);
animation.setStartValue(QRect(0, 0, 100, 30));
animation.setEndValue(QRect(250, 250, 100, 30));

animation.setEasingCurve(QEasingCurve::OutBounce);

animation.start();

在这里,动画将遵循一条曲线,使其像球一样反弹,就像从开始位置到结束位置一样。QEasingCurve有大量曲线供您选择。这些由 QEasingCurve::Type枚举定义。如果需要自定义曲线,也可以自己实现一个,并用QEasingCurve注册。

六、将动画放在一起

应用程序通常会包含多个动画。例如,可能希望同时移动多个图形项目或依次移动多个图形项。

QAnimationGroup的子类是其他动画的容器:

  • QParallelAnimationGroup(并行组)
  • QSequentialAnimationGroup(串行组)

QParallelAnimationGroup 示例:

QPushButton *bonnie = new QPushButton("Bonnie");
bonnie->show();
QPropertyAnimation *anim1 = new QPropertyAnimation(bonnie, "geometry");

QPushButton *clyde = new QPushButton("Clyde");
clyde->show();
QPropertyAnimation *anim2 = new QPropertyAnimation(clyde, "geometry");

QParallelAnimationGroup *group = new QParallelAnimationGroup;
group->addAnimation(anim1);
group->addAnimation(anim2);

group->start();

一个并行组同时播放多个动画。调用它的 start() 函数将启动它管理的所有动画。 


QSequentialAnimationGroup 示例:

QPushButton button("Animated Button");
button.show();

QPropertyAnimation anim1(&button, "geometry");
anim1.setDuration(3000);
anim1.setStartValue(QRect(0, 0, 100, 30));
anim1.setEndValue(QRect(500, 500, 100, 30));

QPropertyAnimation anim2(&button, "geometry");
anim2.setDuration(3000);
anim2.setStartValue(QRect(500, 500, 100, 30));
anim2.setEndValue(QRect(1000, 500, 100, 30));

QSequentialAnimationGroup group;

group.addAnimation(&anim1);
group.addAnimation(&anim2);

group.start();

QSequentialAnimationGroup 按顺序播放动画。 它在上一个动画完成后开始列表中的下一个动画。

由于动画组本身就是一个动画,因此可以将其添加到另一个组中。通过这种方式,可以构建动画的树结构,指定动画的播放时间。


版权声明:本文为kenfan1647原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。