赞
踩
Qt的动画框架由基类QAbstractAnimation以及它的两个子类QVariantAnimation、QAnimationGroup组成。基础动画由QVariantAnimation的子类QPropertyAnimation来设置,再通过将多个QPropertyAnimation和QPauseAnimation组合成为动画组(QParallelAnimationGroup、QSequentialAnimationGroup),完成一个连续的动画。
QPropertyAnimation类能够修改Qt的属性值,如pos、geometry等属性。设置好动画的初值和末值,以及持续的时间后,一个属性动画就基本完成了。
通过修改控件的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();
示例效果:
如果只是需要位移动画的话,修改控件的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();
示例效果:
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();
示例效果:
动画还可以设置时间的插值曲线,默认是linear,即线性运动,通过设置QEasingCurve即可。Qt提供了40种已经定义好的曲线(如果有需要也可以自定义曲线):
示例代码:
pScaleAnimation1->setEasingCurve(QEasingCurve::InOutQuad);
示例效果:
通过将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();
示例效果:
Qt的动画可以设置循环次数,默认的循环是从头再播放一遍,往返运动可以在一个串行动画组中加入初值末值相反的一组动画来实现。
加入并行动画组的动画会同时播放,动画组的总时长是最长的动画所需的时间。
示例代码:
QParallelAnimationGroup *pParallGroup = new QParallelAnimationGroup(this);
pParallGroup->addAnimation(pScaleAnimation1);
pParallGroup->addAnimation(pPosAnimation1);
pParallGroup->addAnimation(pOpacityAnimation1);
pParallGroup->start();
示例效果:
在串行动画组的开始先加入一个QPauseAnimation,再将Pause不同的串行动画组加入并行动画组就可以实现延时效果了。
默认动画是从开始到结束这个方向播放的, 可以设置为从结束到开始播放。
示例代码:
m_group->setDirection(QAbstractAnimation::Backward);
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状态机的介绍。
Qt的状态机框架主要包括三部分:
下面示意图简单演示了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();
示例效果:
使用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.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()); }
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(); }
使用 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(),是为了避免快速点击多次时前次动画没有完成影响本次动画的效果
在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 的值如果大于对应边的一半时就没有圆角效果了
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)
使用 QGraphicsDropShadowEffect 增加阴影效果:
QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect(this);
effect->setBlurRadius(10);
effect->setOffset(0, 1);
indicator->setGraphicsEffect(effect);
这样我们就实现了AnimatedCheckBox!
http://qtdebug.com/qtbook-animated-checkbox/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。