Qt-自定义按钮动画
一、绪论
效果视频:
https://live.csdn.net/v/500458
项目地址:https://github.com/orpheanlive/Qt-animation.git
二、代码
这里新建一个animation类方便移植。
animation.h
#ifndef ANIMATION_H
#define ANIMATION_H#include <QWidget>
#include <QPropertyAnimation>
#include <QPainter>
#include <QEnterEvent>class Animation : public QWidget
{Q_OBJECTQ_PROPERTY(qreal scaleFactor READ scaleFactor WRITE setScaleFactor NOTIFY scaleFactorChanged)public:explicit Animation(QWidget *parent = nullptr);~Animation() override = default;void setText(const QString &text);//设置显示文本void setPicture(const QString &picturePath);//设置图片路径qreal scaleFactor() const { return m_scaleFactor; }//获取当前缩放因子void setScaleFactor(qreal factor);//设置缩放因子signals:void scaleFactorChanged();//缩放因子变化信号protected://重写Qt事件处理函数void enterEvent(QEnterEvent *event) override;//鼠标进入事件void leaveEvent(QEvent *event) override;//鼠标离开事件void paintEvent(QPaintEvent *event) override;//绘制事件private slots:void updateAnimationState();//更新动画状态private:// 私有辅助方法void setupAnimations(); // 初始化动画系统void updatePixmapSize(); // 更新图片尺寸// 成员变量bool m_isHovered = false; // 鼠标悬停状态标志qreal m_scaleFactor = 1.0; // 当前缩放因子qreal m_normalScale = 1.0; // 正常状态缩放值qreal m_hoverScale = 3.0; // 悬停状态缩放值QString m_text; // 显示的文本内容QString m_picturePath; // 图片文件路径QPixmap m_pixmap; // 图片对象QSize m_originalSize; // 图片原始尺寸// 动画对象智能指针std::unique_ptr<QPropertyAnimation> m_enterAnimation; // 进入动画std::unique_ptr<QPropertyAnimation> m_leaveAnimation; // 离开动画
};#endif // ANIMATION_H
animation.cpp
#include "animation.h"
#include <QPainter>// 构造函数:初始化父组件并设置动画系统
Animation::Animation(QWidget *parent): QWidget(parent)
{setupAnimations(); // 调用动画初始化函数
}// 设置动画系统:创建并配置进入和离开动画
void Animation::setupAnimations()
{// 创建进入动画,绑定到scaleFactor属性m_enterAnimation = std::make_unique<QPropertyAnimation>(this, "scaleFactor");m_enterAnimation->setDuration(300); // 设置动画时长300msm_enterAnimation->setEasingCurve(QEasingCurve::OutCubic); // 设置缓动曲线为OutCubic// 创建离开动画,同样绑定到scaleFactor属性m_leaveAnimation = std::make_unique<QPropertyAnimation>(this, "scaleFactor");m_leaveAnimation->setDuration(300);m_leaveAnimation->setEasingCurve(QEasingCurve::OutCubic);// 连接缩放因子变化信号到更新槽函数connect(this, &Animation::scaleFactorChanged, this, &Animation::updateAnimationState);
}// 设置显示文本并触发重绘
void Animation::setText(const QString &text)
{m_text = text;update(); // 请求重绘以显示新文本
}// 设置图片:加载图片文件并更新显示
void Animation::setPicture(const QString &picturePath)
{m_picturePath = picturePath;m_pixmap.load(picturePath); // 从文件路径加载图片// 如果图片加载成功,保存原始尺寸并更新图片大小if (!m_pixmap.isNull()) {m_originalSize = m_pixmap.size();updatePixmapSize();}update(); // 请求重绘以显示新图片
}// 设置缩放因子:如果值发生变化则更新并发出信号
void Animation::setScaleFactor(qreal factor)
{// 使用模糊比较避免浮点数精度问题if (qFuzzyCompare(m_scaleFactor, factor))return;m_scaleFactor = factor;updatePixmapSize(); // 更新图片尺寸emit scaleFactorChanged(); // 发出缩放因子变化信号
}// 鼠标进入事件处理:启动进入动画
void Animation::enterEvent(QEnterEvent *event)
{Q_UNUSED(event) // 标记未使用参数m_isHovered = true; // 设置悬停状态// 配置并启动进入动画:从当前值缩放到悬停值m_enterAnimation->setStartValue(m_scaleFactor);m_enterAnimation->setEndValue(m_hoverScale);m_enterAnimation->start();
}// 鼠标离开事件处理:启动离开动画
void Animation::leaveEvent(QEvent *event)
{Q_UNUSED(event)m_isHovered = false; // 清除悬停状态// 配置并启动离开动画:从当前值缩放到正常值m_leaveAnimation->setStartValue(m_scaleFactor);m_leaveAnimation->setEndValue(m_normalScale);m_leaveAnimation->start();
}// 绘制事件:渲染图片和文本
void Animation::paintEvent(QPaintEvent *event)
{Q_UNUSED(event)// 如果图片为空则直接返回if (m_pixmap.isNull())return;QPainter painter(this);painter.setRenderHint(QPainter::SmoothPixmapTransform); // 设置平滑变换渲染提示// 计算缩放后的尺寸和居中位置const QSize scaledSize = m_originalSize * m_scaleFactor;const QPoint centerPoint((width() - scaledSize.width()) / 2, (height() - scaledSize.height()) / 2);// 绘制缩放后的图片到居中矩形区域painter.drawPixmap(QRect(centerPoint, scaledSize), m_pixmap);// 如果有文本,在底部居中绘制if (!m_text.isEmpty()) {painter.drawText(QRect(0, height() - 20, width(), 20), Qt::AlignCenter, m_text);}
}// 更新动画状态:触发重绘以反映动画变化
void Animation::updateAnimationState()
{update(); // 请求重绘
}// 更新图片尺寸:实际尺寸更新在paintEvent中处理
void Animation::updatePixmapSize()
{// 尺寸更新通过paintEvent自动处理
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = nullptr);~Widget() = default;
};#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "animation.h"
#include <QHBoxLayout>
#include <QVBoxLayout>Widget::Widget(QWidget *parent): QWidget(parent)
{// 设置窗口setStyleSheet("QWidget { background-color: #2b2b2b; color: #87ceeb; }");setWindowTitle("锦瑟的小程序");resize(800, 600);// 创建主布局QHBoxLayout *mainLayout = new QHBoxLayout(this);mainLayout->setSpacing(20);mainLayout->setContentsMargins(50, 50, 50, 50);// 手动创建 Animation 控件Animation *globeWidget = new Animation(this);globeWidget->setText("Globe");globeWidget->setPicture(":/icon/globe.svg");Animation *homeWidget = new Animation(this);homeWidget->setText("Home");homeWidget->setPicture(":/icon/home.svg");Animation *databaseWidget = new Animation(this);databaseWidget->setText("Database");databaseWidget->setPicture(":/icon/database.svg");Animation *downloadWidget = new Animation(this);downloadWidget->setText("Sun");downloadWidget->setPicture(":/icon/sun.svg");// 添加到布局mainLayout->addWidget(globeWidget);mainLayout->addWidget(homeWidget);mainLayout->addWidget(databaseWidget);mainLayout->addWidget(downloadWidget);setLayout(mainLayout);
}
