当前位置: 首页 > 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.lqws.cn/news/107389.html

相关文章:

  • 群晖 NAS 如何帮助培训学校解决文件管理难题
  • Windows下将Nginx设置注册安装为服务方法!
  • 工作服/反光衣检测算法AI智能分析网关V4安全作业风险预警方案:筑牢矿山/工地/工厂等多场景安全防线
  • 2024-2025-2-《移动机器人设计与实践》-复习资料-1-7
  • 当 AI 超越人类:从技术突破到文明拐点的 2025-2030 年全景展望
  • 累乘法求数列的通项公式
  • Python(十五)
  • 本人精通各种语言输出hello world
  • 逆向工程开篇(连载中)
  • 使用seaborn/matplotlib定制好看的confusion matrix
  • ssm学习笔记day04
  • Vue3 + Typescript:类型使用记录 / 类型注解 / 积累
  • PV操作的C++代码示例讲解
  • AI与区块链:数据确权与模型共享的未来
  • Day-15【选择与循环】选择结构-if语句
  • 2025年微信小程序开发:AR/VR与电商的最新案例
  • sourcetree中的mercurial有什么用
  • 用PyTorch从零开始编写DeepSeek-V2
  • ONLYOFFICE 与 LocalAI:在 Ubuntu 上搭建 AI 文档编辑环境
  • 二进制安全-OpenWrt-uBus
  • 黑马程序员TypeScript课程笔记2(11-20)
  • MS1023/MS1224——10MHz 到 80MHz、10:1 LVDS 并串转换器(串化器)/串并转换器(解串器)
  • 【笔记】用命令手动下载并安装 tokenizers 库.whl文件(Python 3.12+)
  • Modbus转Ethernet IP网关助力罗克韦尔PLC数据交互
  • ESP32开发之LED闪烁和呼吸的实现
  • Tiktok App 登录账号、密码、验证码 XOR 加密算法
  • 道可云人工智能每日资讯|北京农业人工智能与机器人研究院揭牌
  • 【leetcode】15.三数之和
  • day20 奇异值SVD分解
  • 重新审视自回归语言模型的知识蒸馏