Qt动画技术汇总

一、Qt动画框架

在这里插入图片描述

Qt的动画框架由基类QAbstractAnimation以及它的两个子类QVariantAnimation、QAnimationGroup组成。基础动画由QVariantAnimation的子类QPropertyAnimation来设置,再通过将多个QPropertyAnimation和QPauseAnimation组合成为动画组(QParallelAnimationGroup、QSequentialAnimationGroup),完成一个连续的动画。

1.1 QPropertyAnimation

QPropertyAnimation类能够修改Qt的属性值,如pos、geometry等属性。设置好动画的初值和末值,以及持续的时间后,一个属性动画就基本完成了。

1.1.1 缩放

通过修改控件的geometry属性可以实现缩放效果,也可以实现位移的动画,该属性的前两个值确定了控件左上角的位置,后两个值确定了控件的大小。
示例代码:

QPropertyAnimation *pScaleAnimation1 = new QPropertyAnimation(ui-。scaleButton, "geometry");
pScaleAnimation1->setDuration(1000);
pScaleAnimation1->setStartValue(QRect(190, 230, 0, 0));
pScaleAnimation1->setEndValue(QRect(120, 160, 140, 140));
pScaleAnimation1->start();

示例效果:
在这里插入图片描述

1.1.2 位移

如果只是需要位移动画的话,修改控件的pos属性即可。pos属性就是控件的左上角所在的位置。
示例代码:

QPropertyAnimation *pPosAnimation1 = new QPropertyAnimation(ui.posButton, "pos");
pPosAnimation1->setDuration(1000);
pPosAnimation1->setStartValue(QPoint(360, 160));
pPosAnimation1->setEndValue(QPoint(360, 350));
pPosAnimation1->setEasingCurve(QEasingCurve::InOutQuad);
pPosAnimation1->start();

示例效果:
在这里插入图片描述

1.1.3 不透明度

Qt的控件没有单独的透明度属性,要修改控件的透明度可以通过QGraphicsOpacityEffect类来实现。
示例代码:

QGraphicsOpacityEffect *pButtonOpacity = new QGraphicsOpacityEffect(this);
pButtonOpacity->setOpacity(1);
ui.opacityButton->setGraphicsEffect(pButtonOpacity);

QPropertyAnimation *pOpacityAnimation1 = new QPropertyAnimation(pButtonOpacity, "opacity");
pOpacityAnimation1->setDuration(1000);
pOpacityAnimation1->setStartValue(1);
pOpacityAnimation1->setEndValue(0);
pOpacityAnimation1->start();

示例效果:
在这里插入图片描述

1.1.4 松弛曲线

动画还可以设置时间的插值曲线,默认是linear,即线性运动,通过设置QEasingCurve即可。Qt提供了40种已经定义好的曲线(如果有需要也可以自定义曲线):
示例代码:

pScaleAnimation1->setEasingCurve(QEasingCurve::InOutQuad);

示例效果:
在这里插入图片描述

1.2 QSequentialAnimationGroup

1.2.1 串行动画分组

通过将QPropertyAnimation或者QPauseAnimation加入,构成一个按加入顺序依次播放的动画组,动画组的总时长是各个加入动画的总和。
示例代码:

QSequentialAnimationGroup *pPosGroup = new QSequentialAnimationGroup(this);
pPosGroup->addPause(500);
pPosGroup->addAnimation(pScaleAnimation1);
pPosGroup->addPause(500);
pPosGroup->addAnimation(pPosAnimation1);
pPosGroup->addPause(500);
pPosGroup->addAnimation(pOpacityAnimation1);
pPosGroup->start();

示例效果:
在这里插入图片描述

1.2.2 往返运动

Qt的动画可以设置循环次数,默认的循环是从头再播放一遍,往返运动可以在一个串行动画组中加入初值末值相反的一组动画来实现。

1.3 QParallelAnimationGroup

1.3.1 并行动画组

加入并行动画组的动画会同时播放,动画组的总时长是最长的动画所需的时间。
示例代码:

QParallelAnimationGroup *pParallGroup = new QParallelAnimationGroup(this);
pParallGroup->addAnimation(pScaleAnimation1);
pParallGroup->addAnimation(pPosAnimation1);
pParallGroup->addAnimation(pOpacityAnimation1);
pParallGroup->start();

示例效果:
在这里插入图片描述

1.3.2 延时播放

在串行动画组的开始先加入一个QPauseAnimation,再将Pause不同的串行动画组加入并行动画组就可以实现延时效果了。

1.3.3 动画方向

默认动画是从开始到结束这个方向播放的, 可以设置为从结束到开始播放。
示例代码:

m_group->setDirection(QAbstractAnimation::Backward);

1.4 QTimeLine

QTimeLine提供了用于控制动画的时间线,可以通过信号槽的方式使用。
示例代码:

ui.progressBar->setValue(0);
ui.progressBar->setRange(0, 100);

/* Construct a 1-second timeline with a frame range of 0 - 100 */
QTimeLine *timeLine = new QTimeLine(1000, this);
timeLine->setFrameRange(0, 100);
connect(timeLine, SIGNAL(frameChanged(int)), ui.progressBar, SLOT(setValue(int)));

/* Clicking the push button will start the progress bar animation */
connect(ui.pushButton, SIGNAL(clicked()), timeLine, SLOT(start()));

示例效果:
在这里插入图片描述

二、动画和状态

当使用状态机时候,我们可以将彼此状态之间用一个或者多个动画关联起来。我们也可以设置各个不同的状态的属性,而不用设置动画的startValues和endValue。这里仅简要介绍Qt状态机,详细可以参考Qt 助手《The State Machine Framework | Qt Core 5.6》对Qt状态机的介绍。

2.1 Qt状态机简介

在这里插入图片描述

Qt的状态机框架主要包括三部分:

  • 以QAbstractState为基类的QState,以及QFinalState,QHistoryState等表示状态的类
  • 以QAbstractTransition为基类的用来表示各类状态转换行为(Transition)的类(包括:事件触发/信号触发/鼠标键盘触发的转换等类别)
  • QStateMachine状态机类

下面示意图简单演示了QStateMachine的API调用:

示例代码:

/* The following snippet shows the code needed to create such a state machine. First, we create the state machine and states:*/

      QStateMachine machine;
      QState *s1 = new QState();
      QState *s2 = new QState();
      QState *s3 = new QState();

/* Then, we create the transitions by using the QState::addTransition() function: */

      s1->addTransition(button, SIGNAL(clicked()), s2);
      s2->addTransition(button, SIGNAL(clicked()), s3);
      s3->addTransition(button, SIGNAL(clicked()), s1);

/* Next, we add the states to the machine and set the machine's initial state: */

      machine.addState(s1);
      machine.addState(s2);
      machine.addState(s3);
      machine.setInitialState(s1);

/* Finally, we start the state machine: */

      machine.start();

示例效果:
在这里插入图片描述

2.2 简单示例

使用QSignalTransition或者QEventTransition类,这两个类都继承自QAbstractTransition,QAbstractTransition提供了方便的函数addAnimation(),可以再状态转换触发时候播放一个或者多个动画animations。下面是一个关联按钮QPushButton位置属性的动画示例:

示例效果:
在这里插入图片描述

示例代码:

QStateMachine *machine = new QStateMachine;

QState *state1 = new QState(machine);
state1->assignProperty(ui.pushButton, "geometry", QRect(30, 250, 100, 30));
machine->setInitialState(state1);

QState *state2 = new QState(machine);
state2->assignProperty(ui.pushButton, "geometry", QRect(250, 250, 100, 30));

QSignalTransition *transition1 = state1->addTransition(ui.pushButton, SIGNAL(clicked()), state2);
transition1->addAnimation(new QPropertyAnimation(ui.pushButton, "geometry"));

QSignalTransition *transition2 = state2->addTransition(ui.pushButton, SIGNAL(clicked()), state1);
transition2->addAnimation(new QPropertyAnimation(ui.pushButton, "geometry"));

machine->start();

三、实例演示

AnimatedCheckBox: 带动画效果的CheckBox

3.1 实现效果

在这里插入图片描述

3.2 实现分析

  • Qt 提供了 QCheckBox,但是无法用 QSS 实现上述样式,而且也没有 indicator 移动的动画效果
  • 继承QCheckBox,有以下好处:(1)可以在使用QCheckBox的地方直接替换为AnimatedCheckBox;(2)不需要维护check box的各种状态;(3)相关的信号槽可以直接使用QCheckBox的
  • 继承QCheckBox,有以下问题:(1)QCheckBox虽然有一个indicator的subcontrol,但是满足不了需求,考虑使用 QLabel 来代替默认的 indicator;(2)默认点击 QCheckBox 的 indicator 或者文字时才能切换选中状态,点击 QCheckBox 上其他空白的地方没有反应,为了实现点击任何地方都能够切换选中状态,需要重写 mousePressEvent;(3)为了不使用 QCheckBox 的默认样式,需要实现一个空的 paintEvent
  • 使用布局管理器无法实现移动indicator,考虑使用绝对坐标定位的方式来控制indicator
  • 需要支持用.qss文件设置样式,不需要重新编译程序,因此不能用QPainter绘制
  • 需要支持控件大小不固定的情况,因此需要重写resizeEvent,根据AnimatedCheckBox的大小和是否checked来计算indicator的大小和位置
  • 使用 QPropertyAnimation 实现动画效果

3.3 控件骨架

AnimatedCheckBox.h

#ifndef ANIMATEDCHECKBOX_H
#define ANIMATEDCHECKBOX_H

#include <QCheckBox>

class QLabel;

class AnimatedCheckBox : public QCheckBox
{
public:
    AnimatedCheckBox(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;
    void resizeEvent(QResizeEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;

private:
    /* AnimatedCheckBox 是否选中的指示器
        checked 为 false 时 indicator 在最左边,为 true 时 indicator 在最右边 */
    QLabel *indicator;
};

#endif // ANIMATEDCHECKBOX_H

AnimatedCheckBox.cpp

#include "AnimatedCheckBox.h"

#include <QStyle>
#include <QLabel>
#include <QMouseEvent>
#include <QApplication>

AnimatedCheckBox::AnimatedCheckBox(QWidget *parent) : QCheckBox (parent)
{
    indicator = new QLabel(this);

    /* 设置样式 */
    this->setMinimumHeight(40);
    this->setAttribute(Qt::WA_StyledBackground, true);
    this->setProperty("class", "AnimatedCheckBox");
    indicator->setProperty("class", "AnimatedCheckBoxIndicator");

    /* AnimatedCheckBox 的选中状态变化时,修改 indicator 的位置 */
    connect(this, &QCheckBox::toggled, [=] {
        int x = this->isChecked() ? this->width() - indicator->width() : 0;
        int y = 0;
        indicator->move(x, y);

        /* checked 属性变化了,更新样式 */
        this->style()->polish(this);
    });
}

/* 重写 paintEvent 方法,清除 QCheckBox 的默认样式 */
void AnimatedCheckBox::paintEvent(QPaintEvent *) {}

/* AnimatedCheckBox 的大小改变时调整 indicator 的位置 */
void AnimatedCheckBox::resizeEvent(QResizeEvent *)
{
    int x = this->isChecked() ? this->width() - indicator->width(): 0;
    int y = 0;
    int w = height();
    int h = w;
    indicator->setGeometry(x, y, w, h);

    /* 设置 AnimatedCheckBox 的最小宽度,避免太窄的时候效果不好 */
    this->setMinimumWidth(height() * 2);
}

/* 点击 AnimatedCheckBox 上的任何地方都切换选中状态,QCheckBox 默认只有点击它的 indicator 或者文字时才进行切换 */
void AnimatedCheckBox::mousePressEvent(QMouseEvent *event)
{
    event->accept();
    setChecked(!isChecked());
}

3.4 设置样式

QtAnimationExample.qss

.AnimatedCheckBox[checked=true ]
{
    background: #2d8cf0;
}
.AnimatedCheckBox[checked=false]
{
     background: #c5c8ce;
}
.AnimatedCheckBoxIndicator
{
    background: white;
}

main.cpp

#include "QtAnimationExample.h"
#include <QtWidgets/QApplication>
#include <QFile>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    /* 设置qss */
    QFile qss(":/QtAnimationExample.qss");
    qss.open(QFile::ReadOnly);
    a.setStyleSheet(qss.readAll());
    qss.close();

    QtAnimationExample w;
    w.show();
    return a.exec();
}

3.5 初步效果

在这里插入图片描述

3.6 使用动画

使用 QPropertyAnimation 给 AnimatedCheckBox 加上动画效果,设置缓冲曲线类型为QEasingCurve::InOutCubic,修改构造函数中的connect部分即可:

QPropertyAnimation *animation = new QPropertyAnimation(indicator, "pos", this);
connect(this, &QCheckBox::toggled, [=] {
    int b = this->contentsMargins().left();
    int x = this->isChecked() ? this->width() - indicator->width() - b : b;
    int y = b;

    animation->stop();
    animation->setDuration(200);
    animation->setEndValue(QPoint(x, y));
    animation->setEasingCurve(QEasingCurve::InOutCubic);
    animation->start();

    this->style()->polish(this); 
});

注意: 在动画开始前调用了 animation->stop(),是为了避免快速点击多次时前次动画没有完成影响本次动画的效果

3.7 实现圆角

在resizeEvent最后增加下面的代码实现圆角效果:

/* 更新 check box 和 indicator 的圆角大小 */
this->setStyleSheet(QString(".AnimatedCheckBox { border-radius: %1px } .AnimatedCheckBoxIndicator { border-radius: %2px }")
                    .arg(this->height() / 2)
                    .arg(indicator->height() / 2));

注意: (1)AnimatedCheckBox 和 indicator 的大小不是固定的,需要在它们的大小改变时动态的计算圆角的半径;(2)QSS 中 border-radius 的值如果大于对应边的一半时就没有圆角效果了

3.8 增加边框

QCheckBox 的 contents margins 的 left 表示边框的宽度, indicator 的位置和大小需要根据边框宽度修改为:

int b = this->contentsMargins().left();
int x = this->isChecked() ? this->width() - indicator->width() - b : b;
int y = b;
int w = height() - b - b;
int h = w;

通过调用 setContentsMargins 来设置边框宽度:

this->setContentsMargins(2, 2, 2, 2)

3.9 增加阴影

使用 QGraphicsDropShadowEffect 增加阴影效果:

QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect(this);
effect->setBlurRadius(10);
effect->setOffset(0, 1);
indicator->setGraphicsEffect(effect);

这样我们就实现了AnimatedCheckBox!

3.10 参考资料

http://qtdebug.com/qtbook-animated-checkbox/

  • 13
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Qt中的StackedWidget是一个容器小部件,它可以用于存储和管理许多其他小部件。在使用StackedWidget进行页面切换时,对于界面切换的动画效果需要考虑。 Qt提供了多种StackedWidget动画,包括滑入/滑出、淡入/淡出、旋转和翻转等。你可以使用setCurrentIndex()函数来实现动画效果。 在其中一个页面切换到另一个页面时,可以使用QPropertyAnimation类实现缓慢的渐变效果。如果你想实现更复杂的动画效果,可以使用QAnimationGroup类组合多个动画。 如果你希望使用第三方库,可以使用QxtStackedWidget的动画效果。该库提供了更多的动画效果,例如侧拉进出、卡片翻转、渐变动画、缩放动画等。 总之,Qt中的StackedWidget动画可以为你的界面切换增加动态和视觉上的吸引力。使用StackedWidget的动画,可以提高用户对应用程序的印象,并帮助你更好地传达信息。 ### 回答2: Qt StackedWidget 动画是指当切换 StackedWidget 中的不同页面时,可以实现动画效果的功能。这个功能可以通过设置页面的切换方式和持续时间来完成。 具体地说,我们可以使用 Qt 动画框架中的 QPropertyAnimation 类来实现这个功能。我们需要创建一个 QPropertyAnimation 对象,并设置目标属性和值以及动画的持续时间。当用户切换页面时,我们可以调用 QPropertyAnimation 对象的 start() 方法来开始动画Qt StackedWidget 支持多种切换动画,包括淡入淡出、从左到右切换、从右到左切换、从上到下切换和从下到上切换等。我们可以使用 setCurrentIndex() 方法来切换页面,并使用 setAttribute() 方法来设置动画属性。 使用 Qt StackedWidget 动画可以提高用户体验,让应用程序更加生动和有趣。但需要注意的是,如果动画太过频繁或过于复杂,可能会影响应用程序的性能。因此,在使用动画效果时,需要根据具体情况进行权衡。 ### 回答3: Qt的StackedWidget支持多种过渡动画效果,包括淡入淡出、滑动、立体翻转等。这些过渡动画可以使应用程序的界面更加美观、直观,增加用户体验。 在使用StackedWidget时,可以设置动画效果的持续时间、缓存类型、方向等。同时,Qt也提供了一些效果较为复杂的动画插件,例如Cute等,可以自定义动画效果,并应用到StackedWidget中。 对于需要自定义动画效果的开发者来说,Qt还提供了QStateMachine和QPropertyAnimation等类,可以使用它们来创建自定义动画效果,并结合StackedWidget进行使用。 总的来说,Qt的StackedWidget动画功能十分强大,可以为应用程序添加美观的界面切换效果,从而提升用户体验。对于开发者来说,掌握这些动画效果的使用方法,可以帮助更好地实现应用程序的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值