I have a bunch of ellipses that initially are lined on top of a path and should move along the QPainterPath. I have it working for the first ellipse but I can't figure out how to get the correct position for the other ellipses.
Is there a way to check if it passed the end of the path and move it back to the beginning?
class Animation : public QAbstractAnimation
{
public:
Animation(const QPainterPath& path, QObject *parent = Q_NULLPTR);
virtual void updateCurrentTime(int ms) override;
virtual int duration() const override;
QPainterPath mPath;
QVector<EllipseGraphicsItem*> mAnimationElements;
};
Animation::Animation (const QPainterPath& path, QObject *parent) : QAbstractAnimation(parent)
, mPath(path)
{
qreal pos = 0;
qreal length = mPath.length();
while (pos < length)
{
qreal percent = path.percentAtLength(pos);
QPointF pointAtPercent = path.pointAtPercent(percent);
pos += 40;
EllipseGraphicsItem * item = new EllipseGraphicsItem(parentItem());
mAnimationElements.append(item);
item->setPos(pointAtPercent);
}
}
void Animation::updateCurrentTime(int ms)
{
QPointF point = mPath.pointAtPercent(qreal(ms) / 6000);
if (mAnimationElements.size() > 0)
mAnimationElements[0]->setPos(point);
for (int i = 0; i < mAnimationElements.size(); i++) {
// how to update each circle's position?
}
}
Start the animation:
QPainterPath path;
path.moveTo(10, 10);
path.lineTo(QPointF(500, 10));
path.lineTo(QPointF(500, 700));
path.lineTo(QPointF(10, 700));
Animation *animation = new Animation(path, this);
animation->setLoopCount(-1);
animation->start();
Imho, it would be easier to use a QGraphicsObject
with a QPropertyAnimation
:
Use a property that varies between 0 and the length of the path and place your elements by calculating their positions from its value and their position in the list.
A quick example :
class AnimatedEllipses: public QGraphicsObject
{
Q_OBJECT
Q_PROPERTY(int progress READ progress WRITE setProgress)
private:
QGraphicsPathItem path;
QList<QGraphicsEllipseItem*> ellipses;
int propProgress;
public:
int progress() const { return propProgress;}
void setProgress(int value)
{
propProgress = value;
int index = 0;
for (QGraphicsEllipseItem* ellipse: ellipses)
{
// Keep value between 0 and length.
int lgt = (propProgress + index * 40) % int(path.path().length());
qreal percent = path.path().percentAtLength(lgt);
++index;
ellipse->setPos(path.path().pointAtPercent(percent));
}
}
AnimatedEllipses(QPainterPath const& path): QGraphicsObject(), path(path), propProgress(0)
{
qreal pos = 0;
qreal length = path.length();
while (pos < length)
{
qreal percent = path.percentAtLength(pos);
QPointF pointAtPercent = path.pointAtPercent(percent);
pos += 40;
QGraphicsEllipseItem * item = new QGraphicsEllipseItem(-10, -10, 20, 20, this);
item->setPos(pointAtPercent);
ellipses << item;
}
QPropertyAnimation* animation = new QPropertyAnimation(this, "progress");
animation->setStartValue(0);
animation->setEndValue(length);
animation->setDuration(10000);
animation->setLoopCount(-1);
animation->start();
}
// QGraphicsItem interface
public:
QRectF boundingRect() const { return path.boundingRect();}
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget){}
};
The modulo allows you to create an infinte loop for each ellipse.