Qt QWiget 实现简约美观的加载动画 第三季

news/2025/5/22 1:51:12/

😃 第三季来啦 😃 这是最终效果:
在这里插入图片描述
只有三个文件,可以直接编译运行

//main.cpp
#include "LoadingAnimWidget.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QGridLayout>
int main(int argc, char *argv[])
{QApplication a(argc, argv);QWidget w;w.setWindowTitle("加载动画 第三季");QGridLayout * mainLayout = new QGridLayout;auto* anim1= new RippleWave;mainLayout->addWidget(anim1,0,0);auto* anim2 = new SlinkyCircle;mainLayout->addWidget(anim2,0,1);auto * anim3 = new ZoomingBalls;mainLayout->addWidget(anim3,1,0);auto* anim4 = new SpotFoldCircle;mainLayout->addWidget(anim4,1,1);w.setLayout(mainLayout);w.show();anim1->start();anim2->start();anim3->start();anim4->start();return a.exec();
}
//LoadingAnimWidget.cpp
#include "LoadingAnimWidget.h"
#include <QDebug>
#include <QPaintEvent>
#include <QPainter>
LoadingAnimBase::LoadingAnimBase(QWidget* parent):QWidget(parent){mAnim.setPropertyName("angle");mAnim.setTargetObject(this);mAnim.setDuration(2000);mAnim.setLoopCount(-1);//run forevermAnim.setEasingCurve(QEasingCurve::Linear);setFixedSize(200,200);mAngle = 0;
}
LoadingAnimBase::~LoadingAnimBase(){}
void LoadingAnimBase::exec(){if(mAnim.state() == QAbstractAnimation::Stopped){start();}else{stop();}
}
void LoadingAnimBase::start(){mAnim.setStartValue(0);mAnim.setEndValue(360);mAnim.start();
}
void LoadingAnimBase::stop(){mAnim.stop();
}
qreal LoadingAnimBase::angle()const{ return mAngle;}
void LoadingAnimBase::setAngle(qreal an){mAngle = an;update();
}SlinkyCircle::SlinkyCircle(QWidget* parent):LoadingAnimBase (parent){mAnim.setEasingCurve(QEasingCurve::InOutCubic);
}
void SlinkyCircle::paintEvent(QPaintEvent *event){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setPen(Qt::NoPen);//画圆弧painter.setBrush(Qt::NoBrush);const int x = this->width();const int y = this->height();QPen pen("black");pen.setCapStyle(Qt::RoundCap);pen.setWidth(x/20);painter.setPen(pen);painter.translate(x/2,y/2);static const qreal spanAngle = 90;//mAngle<=45,要把弧线拉伸出来static const qreal shrinkAngle = 360 - spanAngle;//mAngle==315时,要把弧线收缩起来auto arcRect = this->rect().adjusted(x/5,y/5,-x/5,-y/5);arcRect.translate(-x/2,-y/2);static const int direction = -1;//顺时针if(mAngle < spanAngle){painter.drawArc(arcRect,90 * 16,mAngle * 16 * direction);}else{//弧长是固定的//40 - 320 --> 320 , 280 --> 320if(mAngle > shrinkAngle){painter.drawArc(arcRect,90 * 16,-(360-mAngle)*16 * direction);}else{//我转动的角度是当前角度 - 拉伸门槛,因为有收尾的不动的时间段,占据了一段角度,所以要把转动的角度拉伸一些,//这个比例就是 (360-spanAngle) / (shrinkAngle - spanAngle)const auto delta = (mAngle - spanAngle) * (360-spanAngle) / (shrinkAngle - spanAngle);painter.rotate(-delta * direction);painter.drawArc(arcRect,90 * 16,spanAngle * 16 * direction);}}}
ZoomingBalls::ZoomingBalls(QWidget* parent):LoadingAnimBase (parent){}void ZoomingBalls::paintEvent(QPaintEvent *event){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setPen(Qt::NoPen);//这里可以设置小球的颜色,也可以改成类的成员static const QStringList colorList{"black","black","black","black","black"};const int x = width();const int y = height();const qreal r = x/24;    //小球的半径const qreal rmax = x/10;  //小球最大半径const qreal alphaSpace = 0.8; //小球颜色变化范围static const int startAngle[5] = {0,45,90,135,180};//五个小球开始变化时间点painter.translate(x/6,y/2);for(int i = 0;i < colorList.size();++i){const auto start = startAngle[i];qreal delta = mAngle - start;QColor background = colorList[i];if(delta > 0 && delta < 180){if(delta > 90) delta = 180 - delta;qreal ratio = delta/ 90.0;qreal alpha = 1- ratio *alphaSpace;qreal radius = r + ratio * (rmax - r);background.setAlphaF(alpha);painter.setBrush(QBrush(background));painter.drawEllipse(QPointF(0,0),radius,radius);}else{//不变painter.setBrush(QBrush(background));painter.drawEllipse(QPointF(0,0),r,r);}painter.translate(x/6.0,0);}
}
SpotFoldCircle::SpotFoldCircle(QWidget* parent):LoadingAnimBase (parent){}void SpotFoldCircle::paintEvent(QPaintEvent *event){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setPen(Qt::NoPen);painter.setBrush(QBrush(QColor("black")));static const int startAngle[5] = {0,15,28,40,50}; //小球开始转动的开始时间点static const int restAngle = 360 - startAngle[4]; //小球休息的时间点static const qreal angleStretch = 360.0 / (360 - startAngle[4]); //角度拉伸比例const int x = this->width();const int y = this->height();painter.translate(x/2,y/2);const int radius = x / 16;//小球的半径static const qreal ratioList[5] = {1,0.8,0.64,0.512,0.41}; //小球半径比例列表 ,很明显,小球越来越小for(int i = 0;i < 5;++i){const int start = startAngle[i];const qreal r = radius * ratioList[i];qreal delta = mAngle - start;if(delta > 0 && delta < restAngle){ //要转动起来painter.rotate(delta * angleStretch);painter.drawEllipse(QPointF(0,-y/2 + radius*2),r,r);painter.rotate(-delta * angleStretch);}else{ //停在原地painter.drawEllipse(QPointF(0,-y/2 + radius*2),r,r);}}
}
RippleWave::RippleWave(QWidget* parent):LoadingAnimBase (parent){}void RippleWave::paintEvent(QPaintEvent *event){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setBrush(Qt::NoBrush);QPen pen("black");pen.setWidth(3);//每层波浪的生命周期是6个单位,一共有八层,每层开始时间是前一层波浪的一个单位之后,时间共分为14个单位static const int arr[8] = {10,15,22,31,42,55,70,87};//波浪的半径static const qreal unit = 360.0 / 14;static const qreal startArr[8] = {0,unit,unit*2 , unit*3 ,unit*4,unit*5,unit*6,unit*7};static const qreal lifeSpan = unit*6;const int x = this->width();const int y = this->height();painter.translate(x/2,y/2);for(int i = 0;i < sizeof(arr) / sizeof(int);++i){const auto start = startArr[i];const auto delta = mAngle - start;qreal alpha = 1;if(delta > 0 && delta < lifeSpan){if(delta < unit*2) alpha = delta / (unit*2);if(delta > unit*4) alpha = (lifeSpan - delta) / (unit*2);QColor c("black");c.setAlphaF(alpha);pen.setColor(c);painter.setPen(pen);painter.drawEllipse(QPointF(0,0),arr[i],arr[i]);}else{//什么都不用做}}
}
//LoadingAnimWidget.h
#ifndef LOADINGANIMWIDGET_H
#define LOADINGANIMWIDGET_H
#include <QPropertyAnimation>
#include <QWidget>
class LoadingAnimBase:public QWidget
{Q_OBJECTQ_PROPERTY(qreal angle READ angle WRITE setAngle)
public:LoadingAnimBase(QWidget* parent=nullptr);virtual ~LoadingAnimBase();qreal angle()const;void setAngle(qreal an);
public slots:virtual void exec();virtual void start();virtual void stop();
protected:QPropertyAnimation mAnim;qreal mAngle;
};
class SlinkyCircle:public LoadingAnimBase{//可以伸缩的管子,绕着中心转动,就像弹簧圈,英语叫做slinky,查了好久才查到这个单词,有点强迫症😂Q_OBJECT
public:explicit SlinkyCircle(QWidget* parent = nullptr);
protected:void paintEvent(QPaintEvent *event);
};class ZoomingBalls:public LoadingAnimBase{//五个小球,每个依次变大Q_OBJECT
public:explicit ZoomingBalls(QWidget* parent = nullptr);
protected:void paintEvent(QPaintEvent *event);
};class SpotFoldCircle:public LoadingAnimBase{//五个小球绕中心旋转,可以折叠在一起Q_OBJECT
public:explicit SpotFoldCircle(QWidget* parent = nullptr);
protected:void paintEvent(QPaintEvent *event);
};class RippleWave:public LoadingAnimBase{//8层波纹,从中间逐渐向外变亮,然后变暗Q_OBJECT
public:explicit RippleWave(QWidget* parent = nullptr);
protected:void paintEvent(QPaintEvent *event);
};#endif // LOADINGANIMWIDGET_H

http://www.ppmy.cn/news/1364198.html

相关文章

初学学习408之数据结构--数据结构基本概念

初学学习408之数据结构我们先来了解一下数据结构的基本概念。 数据结构&#xff1a;是相互之间存在一种或多种特定关系的数据元素的集合。 本内容来源于参考书籍《大话数据结构》与《王道数据结构》。除去书籍中的内容&#xff0c;作为初学者的我会尽力详细直白地介绍数据结构的…

【Elasticsearch专栏 16】深入探索:Elasticsearch的Master选举机制及其影响因素分析

Elasticsearch&#xff0c;作为当今最流行的开源搜索和分析引擎&#xff0c;以其分布式、可扩展和高可用的特性赢得了广大开发者的青睐。在Elasticsearch的分布式架构中&#xff0c;集群的稳健性和高可用性很大程度上依赖于其Master节点的选举机制。本文将深入剖析Elasticsearc…

(202402)多智能体MetaGPT入门1:MetaGPT环境配置

文章目录 前言拉取MetaGPT仓库1 仅仅安装最新版2 拉取源码本地安装MetaGPT安装成果全流程展示 尝试简单使用1 本地部署大模型尝试&#xff08;失败-->成功&#xff09;2 讯飞星火API调用 前言 感谢datawhale组织开源的多智能体学习内容&#xff0c;飞书文档地址在https://d…

Centos服务器部署前后端项目

目录 准备工作1. 准备传输软件2. 连接服务器 部署Mysql1.下载Mysql(Linux版本)2. 解压3. 修改配置4. 启动服务另一种方法Docker 部署后端1. 在项目根目录中创建Dockerfile文件写入2. 启动 部署前端1. 在项目根目录中创建Dockerfile文件写入2. 启动 准备工作 1. 准备传输软件 …

openssl3.2 - exp - buf to bio

文章目录 openssl3.2 - exp - buf to bio概述笔记bio_get_length调用端代码函数实现bio_to_buffer END openssl3.2 - exp - buf to bio 概述 不想让程序调用openssl API时, 有文件落地的动作. 如果程序有配置文件要用, 也是自己读文件到buffer, 然后转成BIO给openssl的相关有…

【Python编程+数据清洗+Pandas库+数据分析】

数据分析的第一步往往是数据清洗&#xff0c;这个过程关键在于理解、整理和清洗原始数据&#xff0c;为进一步分析做好准备。Python 语言通过Pandas库提供了一系列高效的数据清洗工具。接下来&#xff0c;该文章将通过一个简单的案例演示如何利用 Pandas 进行数据清洗&#xff…

MySQL之大表删除(基于硬链接方式)

在DROP TABLE的时候&#xff0c;所有进程不管是DDL还是DML都被HANG起&#xff1b;直到DROP结束才继续执行&#xff1b;这是因为INNODB会维护一个全局独占锁&#xff08;在table cache上面&#xff09;&#xff0c;直到DROP TABLE完成才释放。在我们常用的ext3,ext4&#xff0c;…

adb pull 使用

adb pull 是 Android Debug Bridge (ADB) 工具提供的一个命令&#xff0c;用于将设备上的文件拷贝到计算机上。通过 adb pull 命令&#xff0c;实现从 Android 设备上获取文件并保存到本地计算机上。 使用 adb pull 命令的基本语法如下&#xff1a; adb pull <设备路径>…