You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Qt中创建旋转动画?QPropertyAnimation无旋转属性问题

如何在Qt中创建旋转动画?

我发现QPropertyAnimation没有直接的旋转动画属性,尝试用QVariantAnimation实现,但动画效果怪异。更新代码后用paintEvent实现的动画接近正常,但QPixmap会变形。

用户提供的可编译代码如下:

mainwindow.h

#include <QtWidgets/QMainWindow>
#include "ui_MainWindow.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindowClass; };
QT_END_NAMESPACE

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
private:
    Ui::MainWindowClass *ui;
};

mainwindow.cpp

#include "MainWindow.h"
#include "pusharrow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindowClass())
{
    ui->setupUi(this);

    ui->centralWidget->setStyleSheet("background-color: black;");
    PushArrow* label = new PushArrow(this);
    label->setStyleSheet("background-color: green;");
    label->setGeometry(100, 100, 32, 32);
    label->show();


    PushArrow_2* label_2 = new PushArrow_2(this);
    label_2->setStyleSheet("background-color: green;");
    label_2->setGeometry(200, 100, 32, 32);
    label_2->show();
}

pusharrow.h

class PushArrow : public QLabel
{
    Q_OBJECT
public:
    QVariantAnimation* animation;
    QPixmap pixmap;
    QPixmap transformedPixmap;
    bool down = false;

    PushArrow(QWidget* parent = 0) : QLabel(parent) 
    { 
        pixmap = QPixmap("C:/Users/arrow.png");
        setPixmap(pixmap);

        animation = new QVariantAnimation(this);
        animation->setDuration(500); 

        connect(animation, &QVariantAnimation::valueChanged, [=](const QVariant &value)
        {
            qDebug() << value;
            QTransform t;
            t.rotate(value.toReal());
            transformedPixmap = pixmap.transformed(t, Qt::SmoothTransformation);
            setPixmap(transformedPixmap);
        });
    }

    void mouseReleaseEvent(QMouseEvent* event)
    {
        down = !down;
        if (down)
        {
            animation->setStartValue(0.0f);
            animation->setEndValue(-180.0f);
        }
        else
        {
            animation->setStartValue(-180.0f);
            animation->setEndValue(0.0f);
        }

        animation->start();
        QLabel::mouseReleaseEvent(event);
    }
};

class PushArrow_2 : public QLabel
{
    Q_OBJECT
public:
    QPixmap pixmap;
    QPixmap transformedPixmap;

    bool down = false;
    QTimer* m_timer;
    qreal value = 0.0f;
    qreal startValue;
    qreal endValue;

    PushArrow_2(QWidget* parent = 0) : QLabel(parent) 
    { 
        pixmap = QPixmap("C:/Users/arrow.png");
        transformedPixmap = pixmap;

        m_timer = new QTimer(this);
        m_timer->setInterval(2);

        connect(m_timer, &QTimer::timeout, this, [this]
        {
            if (endValue < 0)
            {
                value -= 1.0f;

                if (abs(value) >= abs(endValue))
                {
                    m_timer->stop();
                    value = endValue;
                }
            }
            else
            {
                value += 1.0f;

                if (value >= abs(endValue))
                {
                    m_timer->stop();
                    value = endValue;
                }
            }

            QTransform t;
            t.rotate(value);
            transformedPixmap = pixmap.transformed(t, Qt::SmoothTransformation);
            repaint();
        });

    }

    void mouseReleaseEvent(QMouseEvent* event)
    {
        down = !down;
        if (down)
        {
            startValue = 0.0f;
            endValue = -180.0f;
        }
        else
        {
            startValue = -180.0f;
            endValue = 0.0f;
        }

        m_timer->start();
        QLabel::mouseReleaseEvent(event);
    }

    void paintEvent(QPaintEvent* event) override
    {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing, true);
        painter.drawPixmap(rect(), transformedPixmap, transformedPixmap.rect());
    }
};

问题原因解析

  1. PushArrow的怪异动画:每次调用setPixmap会触发QLabel自动调整大小,旋转后的Pixmap尺寸变大,又被setGeometry固定尺寸,导致Pixmap被拉伸;同时默认旋转中心是Pixmap左上角,动画出现偏移。
  2. PushArrow_2的变形:旋转后的Pixmap尺寸超出Label固定大小,直接用drawPixmap将其强行缩放到Label区域,导致拉伸变形。

正确实现方案

方案1:自定义属性+QPropertyAnimation(推荐)

给自定义控件添加rotation属性,利用Qt的动画框架实现平滑旋转,代码更简洁易维护:

#include <QLabel>
#include <QPropertyAnimation>
#include <QPainter>

class RotatableLabel : public QLabel
{
    Q_OBJECT
    Q_PROPERTY(qreal rotation READ rotation WRITE setRotation)
public:
    explicit RotatableLabel(QWidget *parent = nullptr) : QLabel(parent), m_rotation(0)
    {
        setFixedSize(32, 32);
        m_pixmap = QPixmap("arrow.png");
    }

    qreal rotation() const { return m_rotation; }
    void setRotation(qreal angle)
    {
        m_rotation = angle;
        update();
    }

    void toggleRotation()
    {
        QPropertyAnimation *anim = new QPropertyAnimation(this, "rotation", this);
        anim->setDuration(500);
        anim->setEasingCurve(QEasingCurve::InOutQuad);
        anim->setEndValue(m_rotation == 0 ? -180 : 0);
        anim->start(QAbstractAnimation::DeleteWhenStopped);
    }

protected:
    void paintEvent(QPaintEvent *event) override
    {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        // 平移到控件中心,确保旋转中心为控件中点
        painter.translate(rect().center());
        painter.rotate(m_rotation);
        painter.translate(-rect().center());
        // 居中绘制Pixmap,避免拉伸
        painter.drawPixmap(rect().center() - m_pixmap.rect().center(), m_pixmap);
    }

    void mouseReleaseEvent(QMouseEvent *event) override
    {
        toggleRotation();
        QLabel::mouseReleaseEvent(event);
    }

private:
    qreal m_rotation;
    QPixmap m_pixmap;
};

方案2:修复现有PushArrow_2代码

如果要保留原有实现逻辑,需修改两点:

  1. 调整旋转中心为Pixmap中点
  2. 绘制时避免拉伸,保持Pixmap原始比例

修改后的关键代码:

// 定时器槽函数优化
connect(m_timer, &QTimer::timeout, this, [this]
{
    qreal step = endValue > value ? 1.0f : -1.0f;
    value += step;
    if ((step > 0 && value >= endValue) || (step < 0 && value <= endValue)) {
        value = endValue;
        m_timer->stop();
    }
    update(); // 直接触发重绘,无需提前转换Pixmap
});

// paintEvent重写
void paintEvent(QPaintEvent* event) override
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    // 平移到Pixmap中心旋转
    painter.translate(rect().center());
    painter.rotate(value);
    painter.translate(-m_pixmap.width()/2, -m_pixmap.height()/2);
    painter.drawPixmap(0, 0, m_pixmap);
}

// mouseReleaseEvent简化
void mouseReleaseEvent(QMouseEvent* event)
{
    down = !down;
    endValue = down ? -180.0f : 0.0f;
    m_timer->start();
    QLabel::mouseReleaseEvent(event);
}

内容的提问来源于stack exchange,提问作者Cesar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 05:25:34