当前位置: 首页 > news >正文

QT实现动画翻转效果

文章目录

  • 源码地址,环境:QT5.15,MinGW32
  • 实现思路
  • 先创建两个窗口
  • 把界面添加进QStackWidget
  • 翻转按钮被点击
    • 翻转效果实现类
    • 动画效果
    • 动画播放过程

源码地址,环境:QT5.15,MinGW32

实现思路

翻转动画持续时间
QPropertyAnimation::setDuration(700);
翻转动画起始值
QPropertyAnimation::setStartValue(m_startVal);
翻转动画结束值
QPropertyAnimation::setEndValue(m_endVal);

通过这三个值计算出动画执行过程的一个中间值 iRotateVal

按照这个中间值去播放相应的画面
比如

  • 当旋转角度在0到90度之间时,绘制当前页面(即当前显示的页面),并绕Y轴旋转iRotateVal角度的画面
  • 当旋转角度在90到180度之间时,绘制下一个页面(m_nextPageIndex),并绕Y轴旋转iRotateVal+180度的画面
  • 当旋转角度在-180度到-90度之间时,同样绘制下一个页面,并绕Y轴旋转iRotateVal+180度的画面
  • 当旋转角度在-90度到0度之间时,绘制当前页面,并绕Y轴旋转iRotateVal度的画面

连续的不同角度的图像的绘制就形成了动画

然后翻转的两个画面作为两个窗口放到QStackedWidget里,当动画播放结束以后直接重绘窗口界面

先创建两个窗口

一样的方法
在这里插入图片描述

Page01::Page01(QWidget *parent) :QWidget(parent),ui(new Ui::Page01)
{ui->setupUi(this);
}Page01::~Page01()
{delete ui;
}void Page01::paintEvent(QPaintEvent *event)
{QPainter p(this);QPixmap pix(":/images/1.png");p.drawPixmap(QRect(0,0,500,500),pix);}void Page01::on_pushButton_clicked()
{//点击然后翻转到页面2//发送信号出去emit page01Clicked();
}
class Page01 : public QWidget
{Q_OBJECTpublic:explicit Page01(QWidget *parent = 0);~Page01();signals:void page01Clicked();protected:void paintEvent(QPaintEvent *event);private slots:void on_pushButton_clicked();private:Ui::Page01 *ui;
};

把界面添加进QStackWidget

	AnimationStackedWidget* m_asw;QHBoxLayout *layout = new QHBoxLayout();layout->setContentsMargins(0,0,0,0);m_asw = new AnimationStackedWidget(this);m_page01 = new Page01(this); // AnimationStackedWidget中的第一页m_page02 = new Page02(this); // AnimationStackedWidget中的第二页m_asw->addWidget(m_page01);m_asw->addWidget(m_page02);QObject::connect(m_page01, &Page01::page01Clicked, this, &AnimationDialog::onPage01Clicked);QObject::connect(m_page02, &Page02::page02Clicked, this, &AnimationDialog::onPage02Clicked);layout->addWidget(m_asw);this->setLayout(layout);

翻转按钮被点击

翻转效果实现类

没有这一句无法实现动画播放效果
Q_PROPERTY(float rotateVal READ rotateVal WRITE setRotateVal)

class AnimationStackedWidget : public QStackedWidget
{Q_OBJECT//Qt提供的属性系统,独立于编译器和平台的库//Qt属性是基于元数据对象系统 信号和槽//需要用moc进行编译,需要继承QObjectQ_PROPERTY(float rotateVal READ rotateVal WRITE setRotateVal)//Q_PROPERTY 是 Qt 框架中用于声明类属性的宏,它允许对象属性在 Qt 的元对象系统中被暴露,//从而支持诸如属性动画、属性绑定、信号与槽机制等功能public:AnimationStackedWidget(QWidget *parent = 0);~AnimationStackedWidget();//动画效果void animation(int pageIndex);//动画更新过程中​​,rotateVal() ​​不会被自动调用​​,因为动画通过 setRotateVal() 直接写入值。
//读取属性值由​​开发者手动调用​​或​​框架初始阶段自动调用​​(如确认起始状态)。
//若未显式设置起始值(setStartValue()),动画会调用 rotateVal() 获取当前值作为起始点float rotateVal() {return iRotateVal;}void setRotateVal(float val) {iRotateVal = val;} //设置旋转值float startVal() {return m_startVal;}void setStartVal(float val) {m_startVal = val;}float endVal() {return m_endVal;}void setEndVal(float val) {m_endVal = val;}protected:void paintEvent(QPaintEvent *event);private slots:void onValueChanged(const QVariant &value);void onFinished();private:float iRotateVal;   //旋转值int m_startVal;     //动画起始值int m_endVal;       //动画结束值int m_nextPageIndex;    //下一页
};
void AnimationDialog::onPage01Clicked()
{m_asw->setStartVal(0);   //翻转的起始值m_asw->setEndVal(180);     //翻转的结束值//m_asw实现动画效果m_asw->animation(1);
}

动画效果

注意currentWidget()->hide();这一步很重要

void AnimationStackedWidget::animation(int pageIndex)
{m_nextPageIndex = pageIndex;//propertyNameQPropertyAnimation *animation = new QPropertyAnimation(this, "rotateVal");//设置动画持续时间animation->setDuration(700);//设置动画的缓动曲线为线性模式。缓动曲线控制着动画在执行过程中的速度变化,线性模式意味着动画会以恒定的速度进行animation->setEasingCurve(QEasingCurve::Linear);/*QEasingCurve::InQuad:加速运动QEasingCurve::OutQuad:减速运动QEasingCurve::InOutQuad:先加速后减速*///设置动画起始值//把 AnimationStackedWidget 里的setStartValue到 QPropertyAnimation 里animation->setStartValue(m_startVal);//设置动画结束值animation->setEndValue(m_endVal);//QPropertyAnimation::valueChanged 当属性动画在运行期间属性值"rotateVal"发生变化时QObject::connect(animation, &QPropertyAnimation::valueChanged, this, &AnimationStackedWidget::onValueChanged);QObject::connect(animation, &QPropertyAnimation::finished, this, &AnimationStackedWidget::onFinished);//堆叠窗口(QStackedWidget)默认情况下会显示当前部件//但是,在动画过程中,我们希望由我们自己来控制绘制,而不是让窗口系统自动绘制当前部件。currentWidget()->hide();//!!!/*
动画启动后​​:QPropertyAnimation 会根据预设的 ​​起始值​​、​​结束值​​、​​持续时间​​ 和 ​​缓动曲线​​(默认未设置时使用线性变化),生成中间值序列
*/animation->start();//非阻塞调用​​:这个调用会立即返回,不会等待动画完成//​​异步执行​​:实际的动画是在Qt事件循环中逐步执行的
}

动画播放过程

  1. 启动动画​​(animation->start()):
    未设置起始值则调用 rotateVal() 获取初始值。
  2. ​​每帧动画​​:
    QPropertyAnimation 每帧自动计算(基于时间、缓动曲线等)并通过 setRotateVal() 更新
  3. ​​调用 setRotateVal(val)​​ 更新 iRotateVal。
    ​​触发 valueChanged 信号​​ → 执行 onValueChanged 槽函数。
  4. ​​动画结束​​:
    触发 finished 信号 → 执行 onFinished 槽函数。

所以根据上面槽函数绑定情况可得

当计算出中间值iRotateVal以后就会自动调用 setRotateVal(),然后会触发onValueChanged,rapaint()会触发paintEvent()实现重绘

void AnimationStackedWidget::onValueChanged(const QVariant &value)
{//值改变了以后//立即强制重绘窗口部件。它会直接调用paintEvent()函数,不进行任何优化//如果在短时间内多次调用repaint(),会导致多次重绘,可能影响性能。repaint();//update();//标记窗口部件为 “需要重绘”,但不会立即执行。//Qt 会将重绘请求放入事件队列中,等到下一次事件循环时合并所有的重绘请求,然后只进行一次实际的重绘操作。//这样可以避免不必要的重复绘制,提高性能。
}

按照计算出的中间值绘制图像旋转各角度的画面

void AnimationStackedWidget::paintEvent(QPaintEvent *event)
{QPainter p(this);if (iRotateVal > 0 && iRotateVal <= 90) {
//当旋转角度在0到90度之间(不包括0,包括90)时,绘制当前页面(即当前显示的页面),并绕Y轴旋转iRotateVal角度QPixmap pix(currentWidget()->size());currentWidget()->render(&pix);//旋转QTransform transform;transform.translate(width()/2,0);transform.rotate(iRotateVal, Qt::YAxis);p.setTransform(transform);p.drawPixmap(-1*width()/2, 0,pix);} else if (iRotateVal > 90 && iRotateVal <= 180) {
//当旋转角度在90到180度之间(不包括90,包括180)时,绘制下一个页面(m_nextPageIndex),并绕Y轴旋转iRotateVal+180度QPixmap pix(widget(m_nextPageIndex)->size());widget(m_nextPageIndex)->render(&pix);//旋转QTransform transform;transform.translate(width()/2,0);transform.rotate(iRotateVal+180, Qt::YAxis);p.setTransform(transform);p.drawPixmap(-1*width()/2, 0,pix);}else if (iRotateVal > -180 && iRotateVal <= -90) {
//当旋转角度在-180度到-90度之间(不包括-180,包括-90)时,同样绘制下一个页面,并绕Y轴旋转iRotateVal+180度QPixmap pix(widget(m_nextPageIndex)->size());widget(m_nextPageIndex)->render(&pix);//旋转QTransform transform;transform.translate(width()/2,0);transform.rotate(iRotateVal+180, Qt::YAxis);p.setTransform(transform);p.drawPixmap(-1*width()/2, 0,pix);} else if (iRotateVal > -90 && iRotateVal < 0) {
//当旋转角度在-90度到0度之间(不包括0,包括-90)时,绘制当前页面,并绕Y轴旋转iRotateVal度QPixmap pix(currentWidget()->size());currentWidget()->render(&pix);//旋转QTransform transform;transform.translate(width()/2,0);transform.rotate(iRotateVal, Qt::YAxis);p.setTransform(transform);p.drawPixmap(-1*width()/2, 0,pix);}}

动画播放结束后直接设置QStackWidget的另一个界面

void AnimationStackedWidget::onFinished()
{//动画结束widget(m_nextPageIndex)->show();//用于将指定的 QWidget 控件提升到其父窗口控件的最前面显示widget(m_nextPageIndex)->raise();setCurrentWidget(widget(m_nextPageIndex));
}
http://www.xdnf.cn/news/787627.html

相关文章:

  • Docker 镜像深度剖析:构建、管理与优化
  • 多模态知识图谱可视化构建(neo4j+python+flask+vue环境搭建与示例)
  • 秋招准备-数据结构
  • 前端面试题之Class详解
  • @Resource和@Autowire
  • 《前端面试题:CSS预处理器(Sass、Less等)》
  • 代码训练LeetCode(19)轮转数组
  • 【学习记录】深入解析 AI 交互中的五大核心概念:Prompt、Agent、MCP、Function Calling 与 Tools
  • 全球常用地理信息、遥感数据处理软件介绍(单机版、在线云平台)
  • LeetCode 高频 SQL 50 题(基础版) 之 【高级查询和连接】· 下
  • 【Typst】5.文档结构元素与函数
  • 突破视觉认知边界VisionReasoner:用强化学习统一视觉感知与推理的全能框架
  • 防火墙在OSI模型中的层级工作(2025)
  • 动态规划十大经典题型状态转移、模版等整理(包括leetcode、洛谷题号)
  • Dify-5:Web 前端架构
  • Spring 统一异常处理怎么做?
  • QT 5.9.2+VTK8.0实现等高线绘制
  • Ubuntu中SSH服务器安装使用
  • VMWare安装常见问题
  • MATLAB仿真生成无线通信网络拓扑推理数据集
  • HTTP连接管理——短连接,长连接,HTTP 流水线
  • 【notepad++】如何设置notepad++背景颜色?
  • 在Oxygen编辑器中使用DeepSeek
  • typescript的Interface和Type
  • DPO 算法
  • Gradle依赖管理全面指南:从基础到高级实践
  • Spring Boot整合Druid与Dynamic-Datasource多数据源配置:从错误到完美解决
  • 力扣HOT100之多维动态规划:1143. 最长公共子序列
  • ArrayList 类
  • Generate Permutation