Qt animate椭圆沿圆

Qt animate ellipses along the circle

我是 Qt 的新手,我对如何使用 QGraphicsScene 感到困惑。如果我在场景中添加 10 个椭圆并沿路径设置动画,我该怎么做?它变得更加复杂,因为如果我沿着椭圆绘制了 10 个椭圆,并且我想为这些椭圆设置动画,以便它们远离椭圆的中心。在图片中,您可以看到省略号。这些是用 QPainter 绘制的 我还没有想出如何将它们添加到场景中,但我希望灰色椭圆在内圈和外圈之间移动。我已经看过一些例子,但不能真正适合我的情况。

使用QPainter绘制椭圆的代码:

QPainter drElps;
drElps.setBrush(QBrush(QColor(0xaf, 0xaf, 0xaa)));
drElps.setPen(QPen(QColor(0xaf, 0xaf, 0xaa), 2));
double nrElps = 30;
int degree = (int)(360./nrElps);
painter.translate(QPointF(big_elps_circle_center));
while(--nrElps){
 painter.drawEllipse(0, -60, 3, 3);
 painter.rotate(degree);
}
painter.translate(QPOintF(back_to_origin));

如果您使用 QPainter 绘制它们,那么您就成功了。 QGraphivsScene的重点是继承自QGraphicsItem,把需要画的画在paintEvent上,添加到场景中。

您在 paintEvent 上绘制了超过 1 个项目 - 所以您是靠自己的 - QGraphivsScene 无法帮助您。

现在,如何正确地做到这一点:

  • 继承自 QGraphicsScene 或 QGraphivsView
  • 继承自 QGraphicsEllipseItem 并创建一个 MovableEllipseItem
  • 在 MovableEllipseItem 构造函数中,创建一个 QTimer,将其 timeout() 信号与您将定义的移动槽连接

在你的移动槽上,计算椭圆应该在哪里并移动(x,y)它。

您可以使用自定义图形项、QGraphicsScene::advance 机制以及 QTimeLine 来控制动画。

大纲是这样的:

QGraphicsScene *sc = new QGraphicsScene;
QTimeLine *tl = new QTimeLine(1000);
tl->setFrameRange(-20, 20);
connect(tl, &QTimeLine::frameChanged, sc, &QGraphicsScene::advance);
connect(tl, &QTimeLine::finished, tl, &QTimeLine::toggleDirection);
connect(tl, &QTimeLine::finished, tl, &QTimeLine::start);

for (int i = 0; i < 30; ++i) {
    sc->addItem(new AnimItem(360./30*i, tl));
}

在自定义 AnimItem 中,您实现 drawing/animation 逻辑。一个好的基础是 QGraphicsEllipseItem。例如:

AnimItem::AnimItem(qreal angle, QTimeLine *timer, QGraphicsItem *parent)
    : QGraphicsEllipseItem(0, 0, 3, 3, parent),
      m_timer(timer)
{
    QTransform t;
    t.rotate(angle);
    t.translate(0, -120);
    setTransform(t);
    setBrush(QBrush(QColor(0xaf, 0xaf, 0xaa)));
}

void AnimItem::advance(int phase)
{
    if (phase == 1) {
        QTransform t = transform();
        t.translate(0, m_timer->currentFrame()/5);
        setTransform(t);
    }
}

Qt有一组class面向动画的,为此你必须首先创建一个继承自QGraphicsObject的对象,在这个class中你必须实现方法paintboundingRect.

ellipseobject.h

#ifndef ELLIPSEOBJECT_H
#define ELLIPSEOBJECT_H

#include <QGraphicsObject>

class EllipseObject : public QGraphicsObject
{
    Q_OBJECT
public:
    EllipseObject(QGraphicsItem * parent = 0);

    void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
    QRectF boundingRect() const;
};

#endif // ELLIPSEOBJECT_H

ellipseobject.cpp

#include "ellipseobject.h"

#include <QPainter>

EllipseObject::EllipseObject(QGraphicsItem *parent):QGraphicsObject(parent)
{

}

void EllipseObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option)
    Q_UNUSED(widget)
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setPen(QColor(0xaf, 0xaf, 0xaa));
    painter->setBrush(QBrush(QColor(0xaf, 0xaf, 0xaa)));
    QRectF rect = boundingRect();
    painter->drawEllipse(rect);
}

QRectF EllipseObject::boundingRect() const
{
    return QRectF(-4, -4, 8, 8);
}

然后我们可以使用 QPropertyAnimation class,这样我们就可以为位置设置动画。

EllipseObject *item = new EllipseObject;
QPropertyAnimation *animation = new QPropertyAnimation(item, "pos");
for(int j = 0; j < p; j++){
    animation->setKeyValueAt( 1.0*j/(p-1),
                               (r+ delta*sin(2*M_PI*j/p) )*QPointF(qSin(2*M_PI*i/number_of_items), qCos(2*M_PI*i/number_of_items)));
}
animation->setDuration(2000);

因为我们有几个并行动画的元素,我们可以使用 QParallelAnimationGroup 来处理每个动画。

group = new QParallelAnimationGroup(this);
[...]
group->addAnimation(animation);

如果我们想要连续,我们可以编写以下代码。

group->start();
connect(group, &QParallelAnimationGroup::finished,[=](){
    group->start();
});

完整代码为here.