Qt自定义动画插值函数

这篇文章展示了如何使用自定义的类型实现动画。自定义类型的动画效果使用QVariantAnimation类实现,需要实现一个插值函数。Qt提供了多个接口,主要有如下两种方法:

  1. 实现QVariant QVariantAnimation::interpolated(const QVariant &from, const QVariant &to, qreal progress) const函数;
  2. 使用template <typename T> void qRegisterAnimationInterpolator(QVariant (*func)(const T &, const T &, qreal))函数或void QVariantAnimation::registerInterpolator(Interpolator func, int interpolationType)函数注册自定义插值函数。

本例使用第一种方法,实现了一个按钮在两点之间沿着圆弧从起始点运动到结束点的效果。截图示意如下:

头文件。自定义类型是MMyType,我们重写了interpolated(...)函数:

struct MMyType
{
    QPointF pos;
};

Q_DECLARE_METATYPE(MMyType);

class MDiyAnimation : public QVariantAnimation
{
    Q_OBJECT

public:
    MDiyAnimation(QObject *parent = 0);

private:
    QVariant interpolated(const QVariant &from, const QVariant &to, qreal progress) const override;
};

CPP文件:

MDiyAnimation::MDiyAnimation(QObject *parent) : 
    QVariantAnimation(parent)
{
}

QVariant MDiyAnimation::interpolated(const QVariant &from, const QVariant &to, qreal progress) const
{
    MMyType ifrom = from.value<MMyType>();
    MMyType ito = to.value<MMyType>();

    QPointF ftvec = ifrom.pos - ito.pos;
    qreal radius = 0.5 * qSqrt(QPointF::dotProduct(ftvec, ftvec));
    QPointF center = (ifrom.pos + ito.pos) * 0.5;
    qreal angle = qAtan2(ftvec.y(), ftvec.x()) + M_PI * progress;
    MMyType newPos;
    newPos.pos.setX(center.x() + radius * qCos(angle));
    newPos.pos.setY(center.y() + radius * qSin(angle));
    return QVariant::fromValue<MMyType>(newPos);
}

在主窗口构造函数使用如下。代码中QtTest是主窗口类,ui.pbTest是按钮控件:

QtTest::QtTest(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);

    MDiyAnimation* ani = new MDiyAnimation(this);
    connect(ani, &MDiyAnimation::valueChanged, this, [this](const QVariant& v) 
    {
        QPointF pp = v.value<MMyType>().pos;
        ui.pbTest->setGeometry(pp.x(), pp.y(), ui.pbTest->width(), ui.pbTest->height());
    });
    ani->setStartValue(QVariant::fromValue<MMyType>({ ui.pbTest->pos() }));
    ani->setEndValue(QVariant::fromValue<MMyType>({ QPointF(400, 400) }));
    ani->setDuration(5000);
    ani->setLoopCount(-1);
    ani->start();
}

 

posted @ 2024-03-06 09:12  兜尼完  阅读(99)  评论(0编辑  收藏  举报