即时更改 QPropertyAnimation 持续时间

Change QPropertyAnimation duration on the fly

我正在使用 解决方案来为 QPainterPath 上的椭圆设置动画。 但我需要慢慢增加或减少动画的速度。 我创建了一个计时器并为动画设置了一个新的持续时间,但结果是一个断断续续的动画,因为省略号从头开始。 有没有更好的方法来完成这个?

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){}
};

您可以使用自定义 QEasingCurve 来做到这一点。这是进度条值的一个小例子。

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;
};

初始化

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
}

最有趣的是自定义 EasingCurve

的函数 myEasingFunction
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;
}

并更改计时器的持续时间

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
}