当前位置: 首页 > 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));
}

相关文章:

  • 【嵌入式(2)深入剖析嵌入式开发:从基础到实战】
  • Spring中@Controller和@RestControlle注解的区别
  • Python爬虫监控程序设计思路
  • JVM-内存结构
  • ​​食品电商突围战!品融电商全平台代运营,助您抢占天猫京东抖音红利!
  • Scrapy爬虫框架Spiders爬虫脚本使用技巧
  • Halcon光度立体法
  • Python训练第四十三天
  • DHCP 动态主机配置协议(Dynamic host configuration protocol)逐层封装过程: DHCP --> UDP --> IP
  • 相机Camera日志分析之二十四:高通相机Camx 基于预览1帧的process_capture_request三级日志分析详解
  • KITTI数据集(计算机视觉和自动驾驶领域)
  • Java编程之建造者模式
  • 项目课题——基于ESP32的智能插座
  • 24.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--认证微服务
  • 英福康INFICON VGC501, VGC502, VGC503 单通道、双通道和三通道测量装置
  • AspectJ 在项目中的集成应用代码案例
  • VR教育:开启教育新时代的钥匙
  • Rhino插件大全下载指南:解锁犀牛潜能,提升设计效率
  • 基于大模型的慢性硬脑膜下血肿预测与诊疗系统技术方案
  • LabVIEW基于 DataSocket从 OPC 服务器读取数据
  • 中国水利教育培训网站/无锡百度竞价
  • 浙江有限公司网站/万能优化大师下载
  • 贷款超市网站开发/搜索引擎的两个基本方法
  • 中小学学校网站建设/百度营销app
  • 中天建设集团有限公司资质等级/人员优化是什么意思
  • 一键建站模板/网络营销公司名字