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

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

//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 forever
    mAnim.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 --> 320
        if(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_OBJECT
    Q_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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/409983.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《Docker 简易速速上手小册》第8章 Docker 在企业中的应用(2024 最新版)

文章目录 8.1 Docker 在开发环境中的应用8.1.1 重点基础知识8.1.2 重点案例&#xff1a;Python Web 应用开发环境8.1.3 拓展案例 1&#xff1a;Python 数据分析环境8.1.4 拓展案例 2&#xff1a;Python 自动化测试环境 8.2 Docker 在生产环境的实践8.2.1 重点基础知识8.2.2 重点…

R语言在生态环境领域中的应用

R语言作为新兴的统计软件&#xff0c;以开源、自由、免费等特点风靡全球。生态环境领域研究内容广泛&#xff0c;数据常多样而复杂。利用R语言进行多元统计分析&#xff0c;从复杂的现象中发现规律、探索机制正是R的优势。为此&#xff0c;本课程以鱼类、昆虫、水文、地形等多样…

精品基于springboot健身房管理系统-教练会员卡管理

《[含文档PPT源码等]精品基于springboot健身房管理系统[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; Java——涉及技术&#xff1a; 前端使用技术&#xff1a;HTML5,CS…

异常统一处理:Exception(兜底异常)

一、引言 本篇内容是“异常统一处理”系列文章的重要组成部分&#xff0c;主要聚焦于对 Exception&#xff08;兜底异常&#xff09; 的原理解析与异常处理机制&#xff0c;并给出测试案例。 关于 全局异常统一处理 的原理和完整实现逻辑&#xff0c;请参考文章&#xff1a; 《…

docker搭建zookeeper集群

文章目录 1. 集群搭建2. Leader选举3. Zookeeper集群角色 1. 集群搭建 这里我们使用docker-compose 搭建伪集群 version: 3.1 services:zoo1:image: zookeeperrestart: alwayscontainer_name: zoo1ports:- 2181:2181volumes:- /home/zk/zoo1/data:/data- /home/zk/zoo1/datal…

【数据结构初阶 7】二叉树:链式二叉树的基本操作实现

文章目录 &#x1f308; Ⅰ 定义二叉树结点&#x1f308; Ⅱ 创建二叉树结点&#x1f308; Ⅲ 遍历二叉树1. 先序遍历2. 中序遍历3. 后序遍历4. 层序遍历 &#x1f308; Ⅳ 销毁二叉树 &#x1f308; Ⅰ 定义二叉树结点 1. 每个结点都由三部分组成 数据域&#xff1a;存储本结…

【JVM】线上一次fullGC排查思路

fullGC问题背景 监控告警发现&#xff0c;今天开始我们线上应用频繁出现fullGC&#xff0c;并且每次出现后磁盘都会被占满 查看监控 查看监控发现FULLGC的机器均为同一个机房的集器&#xff0c;并且该机房有线上error报错&#xff0c;数据库监控对应的时间点也有异常&#x…

Vue3 路由配置 + 路由跳转 + 路由传参(动态路由传参 + 普通路由传参)

Vue Router&#xff1a; Vue.js 的官方路由。它与 Vue.js 核心深度集成&#xff0c;让用 Vue.js 构建单页应用变得轻而易举。 效果 一、介绍 1、官方文档&#xff1a;https://router.vuejs.org/zh/introduction.html 介绍 | Vue RouterVue.js 的官方路由https://router.vuejs.…

Java/Python/Go不同开发语言基础数据结构和相关操作总结-GC篇

Java/Python/Go不同开发语言基础数据结构和相关操作总结 1. 常见gc方式1.1 gc判断对象是否存活1.2 引用计数法1.2 标记-清除算法1.3 复制算法1.4 标记-压缩算法1.5 分代收集算法 2. java的gc方式以及垃圾回收器2.1 gc方式2.1 gc回收器2.1.1 Serial收集器2.1.2 ParNew收集器2.1.…

SSM---Mybatis查询数据库的功能

Mybatis查询数据库的功能流程&#xff1a; 在maven中加入mybatis依赖&#xff0c;mysql驱动依赖创建一张student表创建表对应的实体类&#xff1a;student类&#xff0c;用来保存表中的每行数据创建持久层的DAO接口&#xff0c;用来定义操作数据库的方法创建这个表对应的sql映…

计算机设计大赛 深度学习图像风格迁移 - opencv python

文章目录 0 前言1 VGG网络2 风格迁移3 内容损失4 风格损失5 主代码实现6 迁移模型实现7 效果展示8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习图像风格迁移 - opencv python 该项目较为新颖&#xff0c;适合作为竞赛课题…

IDEA下新建SpringBoot项目详细步骤

在IDEA下使用Spring Initializer&#xff1a; 一、新建项目&#xff0c;利用阿里云网址https://start.aliyun.com/下载项目&#xff0c;来到Spring Initializer模块&#xff1a; 我的jdk是8&#xff0c;构建Maven类型的项目&#xff0c;Java版本选8&#xff0c;Group为公司名。…

二阶低通滤波器(博途PLC SCL源代码)

在学习滤波器之前我们先了解下截止频率的准确定义,周期正弦信号经过传递函数后的输出信号,其幅值衰减-3dB时对应的频率。-3dB的含义是幅值衰减为原来的约0.707。更多滤波器信号处理相关内容请参看下面文章链接: 1、PLC一阶低通滤波器 https://rxxw-control.blog.csdn.net/…

前端路由与后端路由的区别

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

JSONVUE

1.JSON学习 1.概念: JSON是把JS对象变成字符串. 2.作用: 多用于网络中数据传输. JavaScript对象 let person{name:"张三",age:18}//将JS对象转换为 JSON数据let person2JSON{"name":"张三","age":18}; 3.JS对象与JSON字符串转换…

读人工不智能:计算机如何误解世界笔记03_AlphaGo

1. 人工智能 1.1. “人工智能”这个词听起来就是电影里的意象 1.1.1. 电影《星际迷航&#xff1a;下一代》中栩栩如生的机器人“数据少校” 1.1.2. 电影《2001太空漫游》中的哈尔9000 1.1.3. 电影《她》中的人工智能系统萨曼莎 1.1.4. 漫威系列漫画和电影中钢铁侠的管家贾…

前端常用6种数据加密方式的使用(最详解)

目录 前言 一、6种常用加密方案 1.Base64加密 2.MD5加密&#xff08;不可逆&#xff09; 3.sha256加密 4.sha1加密&#xff08;相比于MD5 安全性高&#xff0c;但是 速度慢&#xff09; 5.AES加密 6.字符串的编码和解码 二、结语 往期回顾 前言 相信大家在工作或面试…

冯诺依曼体系结构 与 操作系统

一、冯诺依曼体系结构 深入理解冯诺依曼体系结构 计算机的出现就是为了解决实际问题, 所以把问题交给计算机&#xff0c;计算机经过处理&#xff0c;得到一个结果反馈给我们&#xff0c;所以这中间就必然涉及到了输入设备&#xff0c;中央处理器(包括运算器和控制器)和输出设备…

SQL数据操作

目标:掌握mysql中数据的增删改查的基本操作 新增数据 查看数据 更新数据 删除数据 1、新增数据 目标:了解数据的新增指令和逻辑&#xff0c;实现数据的入库操作 概念 新增数据:将数据插入到数据表永久存储 新增数据是根据表的字段顺序和数据类型要求将数据存放到数据表中 …

Low Poly Trees Pack - Flowers

包含59种程式化的低聚植物&#xff0c;作为.fbx网格文件和即用型预制件。 包装内含物 59 个独特的低多边形植物预制件 - 50种开花的草本植物 - 6 棵葡萄树 - 3 灌木 产品特点 -所有植物和石头预制件使用单一反照率256x256纹理图集和1种材质。 -三体计数&#xff1a;50-1000 -支…