动态加速旋转齿轮的实现优化版
前言
和上一次"开荒"随心写的相比,这一篇更加注重代码规范以及逻辑优化。
在实际开发中,我们可以使某些小控件"运动"起来。这样可以使得"一动不动"的软件框架看起来更有"生机"。在Qt中有一个动画类很好用,下面是笔者根据QPropertyAnimation类设计出的一个可以旋转运动的齿轮。
准备
齿轮图片:我们需要一张齿轮的图片,图片复杂或者简单根据需求就行,建议图片要透明背景的。
VS2017+Qt插件。
设计方案
静态图片的实现
最基本的UI贴图,我们可以以QGraphicsView作为控件载体,在QGraphicsScene上实现:
cpp
QGraphicsScene *pScene = new QGraphicsScene(this);
m_pUi->m_pPreGraphicsView->setRenderHint(QPainter::Antialiasing);
m_pUi->m_pPreGraphicsView->setScene(pScene);
我们只需要官方库的类就完全能够满足静态图片,不需要单独写一个类。我们将QPixmap贴图在QGraphicsPixmapItem上:
cpp
QPixmap pixmap("图片路径.png");
QGraphicsPixmapItem* m_pPixmapItem = new QGraphicsPixmapItem(pixmap);
m_pPixmapItem->setPos(x,y);//x,y为图片在QGraphicsScene上的坐标位置
m_pScene->addItem(m_pPixmapItem);
至此,一张静态的图片完成。
动态匀速旋转图片的实现
就如前言所说,有时候静态的贴图会使得整个UI界面显得生硬,我们需要引入一些动画,当需要引入动画时,我们需要重写QGraphicsPixmapItem,为了方便演示先简单的写出如下类:
MyAnimatedPixmapItem头文件:
cpp
#include <QApplication>
#include <QGraphicsPixmapItem>
#include <QPropertyAnimation>
class MyAnimatedPixmapItem : public QObject, public QGraphicsPixmapItem
{
Q_OBJECT
Q_PROPERTY(qreal rotation READ rotation WRITE setRotation)
public:
explicit MyAnimatedPixmapItem(const QPixmap &pixmap);
virtual ~MyAnimatedPixmapItem();
private:
QPropertyAnimation *m_pAnimation = nullptr;
}
MyAnimatedPixmapItem源文件:
cpp
#include "MyAnimatedPixmapItem.h"
const int INT_ZERO = 0;
const int INT_CIRCLE_TIME = 3000;
const int INT_ONE_CIRCLE = 360;
const int INT_INFINITE_TIME = -1;
MyAnimatedPixmapItem::MyAnimatedPixmapItem(const QPixmap &pixmap)
: QGraphicsPixmapItem(pixmap)
{
this->setPixmap(pixmap);
this->setTransformOriginPoint(this->boundingRect().center());
m_pAnimation = new QPropertyAnimation(this, "rotation");
m_pAnimation->setDuration(INT_CIRCLE_TIME);//设置动画时间(转动一圈)
m_pAnimation->setEasingCurve(QEasingCurve::Linear);//设置匀速转动
m_pAnimation->setStartValue(INT_ZERO);
m_pAnimation->setEndValue(INT_ONE_CIRCLE);
m_pAnimation->setLoopCount(INT_INFINITE_TIME);//设置成无限循环
m_pAnimation->start();
}
需要注意的是:
①需要调用setEasingCurve(QEasingCurve::Linear)将动画模式设置成匀速旋转。
②需要调用setLoopCount(-1)将该动画设置成无限循环。
至此一个匀速转动效果的动画实现了,适用于无限匀速转动齿轮的需求。
动态匀速旋转图片的实现
如果需求是贴图能够停止能够运动,这样突然就匀速旋转或者急停,动画效果难免有些不连贯。假如现在有更深一层的需求,需要在动画开始时缓慢开始加速。这个时候我们可以引入加速度和减速度,可喜的是,Qt的官方函数已经给我们封装好了加速动画和减速动画:
cpp
pAnimation->setEasingCurve(QEasingCurve::InQuad);//设置加速运动
pAnimation->setEasingCurve(QEasingCurve::OutQuad);//设置减速运动
如果直接冒然将加速替换匀速是不可取的,这样的后果是在一个新周期开始时动画旋转速度是0,然后到动画结束最快,然后速度突然降为0,周而复始。
❌❌
为了让动画显示更具观赏性,我们需要在动画开始事件触发时的第一个周期,动画加速旋转。第一个周期结束后,动画匀速旋转,在触发了动画关闭事件后的下一个周期,动画开始减速旋转直到周期结束。因此,我们需要保证在动画每次完成一轮后都会触发一次事件,用来观察下一个动画中期齿轮应该进行的动画。
因为真实状态可能(比如下位机向上位机传信号开始或停止动画)是瞬间的,而为了让动画更具备连贯性,需要在瞬间触发开始或者停止时有一个加速度到匀速或者减速度到停止的动画效果:
cpp
void MyAnimatedPixmapItem::onAnimationFinished()
{
switch (m_nRotationStatus)
{
case ENUM_ROTATION_STATUS_STOP:
case ENUM_ROTATION_STATUS_DECELERATION:
if(ENUM_TEST_STATUS_RUN == m_nTestStatus)
{
m_pAnimation->setDuration(m_nDoubleRotationTime);
m_pAnimation->setEasingCurve(QEasingCurve::InQuad);
m_nRotationStatus = ENUM_ROTATION_STATUS_ACCELERATE;
m_pAnimation->start();
}
else if (ENUM_TEST_STATUS_STOP == m_nTestStatus)
{
m_nRotationStatus = ENUM_ROTATION_STATUS_STOP;
}
break;
case ENUM_ROTATION_STATUS_ACCELERATE:
case ENUM_ROTATION_STATUS_UNIFORM_SPEED:
if (ENUM_TEST_STATUS_STOP == m_nTestStatus)
{
m_pAnimation->setDuration(m_nDoubleRotationTime);
m_pAnimation->setEasingCurve(QEasingCurve::OutQuad);
m_nRotationStatus = ENUM_ROTATION_STATUS_DECELERATION;
m_pAnimation->start();
}
else if(ENUM_TEST_STATUS_RUN == m_nTestStatus)
{
m_pAnimation->setDuration(m_nRotationTime);
m_pAnimation->setEasingCurve(QEasingCurve::Linear);
m_nRotationStatus = ENUM_ROTATION_STATUS_UNIFORM_SPEED;
m_pAnimation->start();
}
break;
default;
break;
}
有了刚刚的事件处理机制,在各种情况下接收到开始、停止指令时,在结束一轮动画之后都能连贯的进行下一轮动画:
极端情况1:刚刚开始就关闭
极端情况2:刚刚关闭又开始