如何在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()); } };
问题原因解析
- PushArrow的怪异动画:每次调用
setPixmap会触发QLabel自动调整大小,旋转后的Pixmap尺寸变大,又被setGeometry固定尺寸,导致Pixmap被拉伸;同时默认旋转中心是Pixmap左上角,动画出现偏移。 - 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代码
如果要保留原有实现逻辑,需修改两点:
- 调整旋转中心为Pixmap中点
- 绘制时避免拉伸,保持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
相关产品推荐
相关产品推荐

