I'm using this solution to animate ellipses along a QPainterPath
.
But I need to slowly increase or decrease the speed of the animation.
I create a timer and set a new duration for the animation but the result is a choppy animation because the ellipses start from the beginning.
Is there a better way to accomplish this?
class AnimatedEllipses: public QGraphicsObject
{
Q_OBJECT
Q_PROPERTY(int progress READ progress WRITE setProgress)
private:
QGraphicsPathItem path;
QList<QGraphicsEllipseItem*> ellipses;
int propProgress;
QPropertyAnimation* animation;
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;
}
animation = new QPropertyAnimation(this, "progress");
animation->setStartValue(0);
animation->setEndValue(length);
animation->setDuration(10000);
animation->setLoopCount(-1);
animation->start();
QTimer *timer = new QTimer();
connect(timer, SIGNAL(timeout()), this, SLOT(SlotTimeOut()));
timer->start(1000);
}
void SlotTimeOut() {
int newDuration = GetRandomDuration();
animation->setDuration(newDuration);
}
// QGraphicsItem interface
public:
QRectF boundingRect() const { return path.boundingRect();}
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget){}
};
You can do this with custom QEasingCurve
. Here is a small example for progressbar value.
MainWindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void OnChangeDurationTimer();
private:
Ui::MainWindow *ui;
QPropertyAnimation m_animProgress;
};
Initialization
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_animProgress.setTargetObject(ui->progressBar);
m_animProgress.setPropertyName("value");
m_animProgress.setStartValue(0);
m_animProgress.setEndValue(100);
m_animProgress.setDuration(10000);
m_animProgress.setLoopCount(-1);
//setting custom QEasingCurve
QEasingCurve eCurve;
eCurve.setCustomType(myEasingFunction);
m_animProgress.setEasingCurve(eCurve);
m_animProgress.start();
QTimer::singleShot(3000, this, &MainWindow::OnChangeDurationTimer); //timer to change duration
}
Most interesting is function myEasingFunction
for custom EasingCurve
qreal g_offset = 0; //value last animation stopped with
qreal g_offsetLast = 0; //keep current value of animation
qreal myEasingFunction(qreal progress)
{
qreal val = g_offset + progress;
while (val > 1) {
val -= 1; //normalize
}
g_offsetLast = val;
return val;
}
And changing duration on timer
void MainWindow::OnChangeDurationTimer()
{
g_offset = g_offsetLast; //remember stopped value
m_animProgress.stop();
m_animProgress.setDuration((rand() % 10 + 1) * 1000);
m_animProgress.start();
QTimer::singleShot(3000, this, &MainWindow::OnChangeDurationTimer); //next changing
}