Qt QWidget 简约美观的加载动画 第四季

😊 第四季来啦 😊
效果如下:
在这里插入图片描述
只有三个文件,可以直接编译运行的

//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 JellyInBox;
    mainLayout->addWidget(anim1,0,0);

    auto* anim2 = new HorizontalDNA;
    mainLayout->addWidget(anim2,0,1);

    auto * anim3 = new  LoopedRingWithText;
    mainLayout->addWidget(anim3,1,0);

    auto* anim4 = new Radar;
    mainLayout->addWidget(anim4,1,1);

    w.setLayout(mainLayout);
    w.show();
    anim1->start();
    anim2->start();
    anim3->start();
    anim4->start();
    return a.exec();
}

//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 JellyInBox:public LoadingAnimBase{//一个会伸缩的果冻在一个小盒子里面,一开始拉伸,然后移动,然后收缩
public:
    explicit JellyInBox(QWidget* parent = nullptr);
protected:
    void paintEvent(QPaintEvent *event);
};
class HorizontalDNA:public LoadingAnimBase{//两根水平方向的小球链,螺旋滚动,像DNA双分子链
public:
    explicit HorizontalDNA(QWidget* parent = nullptr);
protected:
    void paintEvent(QPaintEvent*);
private:
    void getPosYAlphaScale(const int amplitude,const qreal & offset,qreal & y,qreal & alpha,qreal & scale);
    //根据原始偏移获取当前时间点下的y轴坐标,透明度,和缩放比例
};
class LoopedRingWithText:public LoadingAnimBase{//外面有三圈圆环转动,中间有一个高亮的灯光从左向右移动照亮字符串的一部分
public:
    explicit LoopedRingWithText(QWidget* parent = nullptr);
protected:
    void paintEvent(QPaintEvent*);
};
class Radar:public LoadingAnimBase{//像一个雷达一样扫描,等待耗时操作的结束
public:
    explicit Radar(QWidget* parent = nullptr);
protected:
    void paintEvent(QPaintEvent*);
};

#endif // LOADINGANIMWIDGET_H

//LoadingAnimWidget.cpp
#include "LoadingAnimWidget.h"
#include <QDebug>
#include <QPaintEvent>
#include <QPainter>
#include <QtMath>
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();
}
JellyInBox::JellyInBox(QWidget* parent):LoadingAnimBase (parent){}
void JellyInBox::paintEvent(QPaintEvent *event){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(Qt::NoBrush);

    int x = this->width();
    const int y = this->height();

    //先画一个盒子
    QPen pen("black");
    pen.setWidth(3);
    painter.setPen(pen);
    painter.drawRoundedRect(QRectF(1,0.4375*y,x-2,y/8),y/16.0,y/16.0);

    //画果冻
    pen.setCapStyle(Qt::RoundCap);
    painter.setBrush(Qt::NoBrush);

    const int gap = 4;//果冻和盒子之间的间距
    const int boxGap = 1;//盒子的外间距
    const qreal jh = y/8 - gap*2;//果冻高度
    pen.setWidthF(jh);
    painter.setPen(pen);

    painter.translate(boxGap + gap + jh/2,y/2);
    x -= (boxGap + gap) * 2 + jh;//可以移动的x范围是总宽度 - 两边的两个间距 - 笔头厚度
    const qreal maxw = 0.4*x;//最大的果冻宽度
    int jx = 0;
    qreal jw = 0;

    if(mAngle < 90){
        jw = maxw * mAngle / 90;
    }
    else if(mAngle < 270){
        jw = maxw;
        jx = (x - maxw) * (mAngle - 90) / 180;
    }
    else{
        jx = x - maxw + (mAngle - 270) / 90 * maxw;
        jw = x - jx;
    }
    painter.drawLine(jx,0,jx+jw,0);
}
HorizontalDNA::HorizontalDNA(QWidget* parent):LoadingAnimBase (parent){
    mAnim.setDuration(5000);
}
void HorizontalDNA::getPosYAlphaScale(const int amplitude,const qreal & offset,qreal & y,qreal & alpha,qreal & scale){
    static const qreal a = 2*3.1415926 / 360;
    //qreal x = a*mAngle + offset;//随着时间的流逝,x变大,y踩着着右边的值,看上去就是右边的小球带动左侧的小球移动
    //如果要实现左边的小球带动右侧的小球,要这样
    auto x = -a * mAngle + offset;
    y = qSin(x);
    auto tmp = qCos(x); //随着时间的流逝,x变小,当小球向上移动的时候,透明度变小
    tmp += 1;
    tmp /= 2;
    alpha = 1- tmp;
    scale = alpha / 2 + 0.5;//不要太小了,最小是0.5
    y *= amplitude;//amplitude就是振幅 sin(x)的振幅是1
}
void HorizontalDNA::paintEvent(QPaintEvent*){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::NoPen);

    const int x = width();
    const qreal radius = x / 20.0;//小球的半径
    const qreal y = height();
    const qreal unitx = x / 8.0;//小球的间距
    painter.translate(unitx,y/2);

    static const qreal gap = M_PI / 6;
    static const qreal offsetList[7] = {gap*0,gap*1,gap*2,gap*3,gap*4,gap*5,gap*6};
    QColor ballColor("black");
    for(int i = 0;i < 7;++i){
        qreal bally ,alpha,scale;
        getPosYAlphaScale(y/6,offsetList[i],bally,alpha,scale);
        ballColor.setAlphaF(alpha);
        painter.setBrush(QBrush(ballColor));
        painter.drawEllipse(QPointF(unitx*i,bally),radius*scale,radius*scale);
    }
    static const qreal offsetList2[7] = {gap*6,gap*7,gap*8,gap*9,gap*10,gap*11,0};
    for(int i = 0;i < 7;++i){
        qreal bally ,alpha,scale;
        getPosYAlphaScale(y/6,offsetList2[i],bally,alpha,scale);
        ballColor.setAlphaF(alpha);
        painter.setBrush(QBrush(ballColor));
        painter.drawEllipse(QPointF(unitx*i,bally),radius*scale,radius*scale);
    }
}
LoopedRingWithText:: LoopedRingWithText(QWidget* parent):LoadingAnimBase(parent){
    mAnim.setDuration(6000);
}
void  LoopedRingWithText::paintEvent(QPaintEvent*){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    const int x = width();
    const int y = height();
    //step0:定义一些颜色变量,也可以改成成员变量
    static const QColor bright("turquoise");//最亮的圆环颜色
    static const QColor medium = QColor(bright.red(),bright.green(),bright.blue(),200);
    static const QColor dim = QColor(bright.red(),bright.green(),bright.blue(),160);

    //step1:画一个深色的背景
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(QColor("dimgray")));
    painter.drawRoundedRect(this->rect(),x/12,x/12);
    //step2:画最外面的两个圆环
    painter.translate(x/2,y/2);
    QPen pen(dim);
    pen.setWidth(4);
    pen.setCapStyle(Qt::RoundCap);
    painter.setPen(pen);
    const auto rect1 = QRectF(-0.45*x,-0.45*y,0.9*x,0.9*y);

    static const qreal start = 30;
    static const qreal span = 120;
    auto ang = mAngle;
    painter.rotate(ang);
    painter.drawArc(rect1,start * 16,span * 16);
    painter.drawArc(rect1,(start+180) *16,span * 16);
    painter.resetTransform();

    //step3:画中间的两个圆环
    pen.setColor(medium);
    painter.setPen(pen);
    painter.translate(x/2,y/2);
    const auto rect2 = QRectF(-0.375*x,-0.375*y,0.75*x,0.75*y);
    if(ang > 180) ang -= 180;//周期改成180,比外面的转的快一倍
    painter.rotate(-2*ang);
    painter.drawArc(rect2,start * 16,span * 16);
    painter.drawArc(rect2,(start + 180)*16,span*16);
    painter.resetTransform();

    //step4:画内部的4个圆环
    pen.setColor(bright);
    painter.setPen(pen);
    painter.translate(x/2,y/2);
    const auto rect3 = QRectF(-0.3*x,-0.3*y,0.6*x,0.6*y);
    ang = mAngle;
    static const qreal t3 = 120;//周期改成120,比中间的更快
    while(ang > t3) ang -= t3;
    painter.rotate( ang * 90 / t3);//四个圆环转过90度的时候也就是长短交替一次,要把这个转过的角度乘以调整系数:90/120
    qreal start0 = 15 + (90-15) * (ang / t3);//开始的时候上方圆环起点在15度,变化到90度时弧长为0
    qreal span0 = 180 - 2*start0;
    if(span0 > 0){
        //第一组
        painter.drawArc(rect3,start0*16 , span0*16);
        painter.drawArc(rect3,(start0+180)*16,span0*16);
    }
    start0 = -75 * (ang / t3);//开始的时候,右侧圆环起点是0,弧长为0,它的最大弧长时刻,起点在-75度
    span0 = -start0 * 2;
    if(span0 > 0){
        //第二组
        painter.drawArc(rect3,start0*16,span0*16);
        painter.drawArc(rect3,(start0+180)*16,span0*16);
    }
    painter.resetTransform();

    QFont font("Microsoft YaHei",12,4);
    painter.setFont(font);
    QFontMetrics fm(font);
    static const QString text("Loading...");
    qreal textw = fm.horizontalAdvance(text);
    qreal texth = fm.height();
    QPainterPath pp;
    pp.addText(QPointF( (x - textw)/2,0.5*y + texth/4),font,text);
    painter.setClipPath(pp);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(dim));
    //step5: 先画一个淡色的文本
    painter.drawRect(rect());
    //step6: 最后一步啦,画一个圆灯光
    painter.translate(0,0.5*y);
    const qreal unit = x / 360.0;
    QRadialGradient grad(QPointF(mAngle*unit,0),texth);
    grad.setColorAt(0,bright);
    grad.setColorAt(1,dim);
    painter.setBrush(grad);
    painter.drawEllipse(QPointF(mAngle * unit,0),texth,texth);
}
Radar::Radar(QWidget* parent):LoadingAnimBase(parent){}
void Radar::paintEvent(QPaintEvent*){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    //step1: 画一个淡色背景,很多简约的界面都需要透明背景,这一步可以删掉
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush("deepskyblue"));
    const int x = width();
    const int y = height();
    painter.drawRoundedRect(rect(),x/12,x/12);
    //step2: 画一个雷达
    painter.translate(x/2,y/2);
    QConicalGradient gra(QPointF(0,0),45);
    static const QColor color0("white");
    static const QColor color1(color0.red(),color0.green(),color0.blue(),100);
    gra.setColorAt(0,color0);
    gra.setColorAt(0.2,color1);
    gra.setColorAt(1,"transparent");
    painter.setBrush(gra);
    painter.rotate(mAngle);
    const auto rect = QRectF(-x/4,-y/4,x/2,y/2);
    painter.drawPie(rect,45*16,90*16);

}

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

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

相关文章

亚洲唯一!京东荣获2024年度Gartner供应链技术创新奖背后的创新探索

序言&#xff1a; 序言&#xff1a;2月14日晚间&#xff0c;Gartner公布了2024年度Garter Power of the Profession供应链大奖&#xff0c;京东集团荣获供应链技术创新奖&#xff0c;成为获得该奖项的唯一亚洲企业。Gartner Power of the Profession供应链奖项已经举办十年&am…

驻场人员严重划水,愈演愈烈,要请领导出面吗?

你有没有遇到过团队成员偷懒的情况&#xff1f;比如你们一起完成某个项目目标&#xff0c;干着干着你发现&#xff0c;就只有你和几个核心人员比较上心&#xff0c;很多人都在划水。 你可能会觉得这是因为大家工作态度不好&#xff0c;甚至怀疑他们的人品&#xff0c;忍不住想…

MoonBit支持云原生调试功能

MoonBit 更新 1. 支持云原生调试功能 现在&#xff0c;你可以通过访问try.moonbitlang.cn&#xff0c;直接在浏览器中使用 devtools 调试 MoonBit 程序&#xff0c;无需安装任何软件。具体的使用步骤如下&#xff1a; 2. MoonBit 支持使用 for 关键字定义的函数式循环控制流 …

ShardingJDBC分库分表

目录 ShardingSphere ShardingJDBC客户端分库分表 ShardingProxy服务端分库分表 两者对比 ShardingJDBC分库分表实战 需求 步骤 分片策略汇总 ShardingSphere ShardingSphere最为核心的产品有两个&#xff1a;一个是ShardingJDBC&#xff0c;这是一个进行客户端分库分表…

Linux命令行常用命令

初识shell shell是系统的用户界面&#xff0c;提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行。实际上shell是一个命令解释器&#xff0c;它解释用户输入的命令并且把用户的意图传达给内核。&#xff08;可以理解为用户与内核之间的翻译官…

npm/nodejs安装、切换源

前言 发现自己电脑上没有npm也没有node很震惊&#xff0c;难道我没写过代码么&#xff1f;不扯了&#xff0c;进入正题哈哈…… 安装 一般没有npm的话会报错&#xff1a; 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称而且报这个错&#xff0c;我们执行…

Order By Limit不稳定性

文章目录 前置解决不确定性场景1 Order By索引1.1 背景1.2 不确定性产生原因1.2.1 正常情况下1.2.2 但是 1.3 补充1.4 场景1总结 场景2 Order by id2.1 背景2.2 不会产生不确定性原因1原因2 2.3 推荐使用方式 场景3 filesort3.1 背景3.2 不确定性产生原因3.3 内存排序和磁盘临时…

内衣洗衣机哪个牌子好用?甄选安利四款优质好用的内衣洗衣机

内衣洗衣机是近几年新兴的一种家用电器产品&#xff0c;正日益引起人们的重视。但是&#xff0c;面对市面上品牌繁多、款式繁多的内衣洗衣机&#xff0c;使得很多人都不知道该如何选择。身为一个数码家电博主&#xff0c;我知道这类产品在挑选方面有着比较深入的了解。为此&…

数据结构2月25日

第一道&#xff1a; 第二道&#xff1a; 1、插入到prev和next中间 1.new(struct list_head*)malloc(sizeof(struct list_head*)); if(newNULL) { printf("失败\n"); return; } new->nextprev->next; prev->nextnew; return; 2、删除prve和next…

redis——客户端

Redis是一个典型一对多服务器程序&#xff0c;一个服务器可以与多个客户端进行网络连接&#xff0c;每隔客户端可以向服务器发送命令请求&#xff0c;而服务器则接收并处理客户端发送的命令请求&#xff0c;并向客户端返回命令请求。 通过是一个I/O多路复用技术实现的文件事件处…

3分钟快速实现串口PLC远程下载程序操作说明

3分钟快速实现串口PLC远程下载程序操作说明 搜索蓝蜂物联网官网&#xff0c;即可免费领取样机使用&#xff01;&#xff01;先到先得&#xff01;&#xff01;&#xff01; 一. 适用产品型号 其余型号网关此功能正在开发中&#xff0c;敬请期待。 二. 远程下载功能使用流程 …

数据结构--双向链表专题

目录 1. 双向链表的结构2. 实现双向链表预先的准备初始化尾插、头插尾删、头删查找在pos位置之后插⼊数据删除pos位置的数据 3. 顺序表和双向链表的分析 1. 双向链表的结构 注意&#xff1a;这里的“带头”跟前面我们说的“头结点”是两个概念&#xff0c;为了更好的理解直接称…

Nginx的反向代理:实现灵活的请求转发和内容缓存

一、引言&#xff1a;代理服务器的简介 本节介绍代理服务器的基本配置。学习如何通过不同协议将 NGINX 请求传递给代理的服务器&#xff0c;修改发送到代理服务器的客户端请求标头&#xff0c;以及配置来自代理服务器的响应缓冲。 代理通常用于在多个服务器之间分配负载&…

tigramite教程(二)生物地球科学案例研究

文章目录 数据生成与绘图因果发现分析平稳性假设、确定性、潜在混杂因素结构假设参数假设使用PCMCIplus的滑动窗口分析聚合因果图非参数因果效应估计假设的图形和调整集干预的真实情况假设的参数模型和因果效应的估计使用关于图的不同假设进行估计非因果估计项目地址 这个文件…

力扣随笔之颜色分类(中等75)

思路&#xff1a;定义两个指针划分left&#xff0c;right划分三个区域left左边是红色区域&#xff0c;right右边是蓝色区域&#xff0c;left和right之间是白色区域&#xff1b;定义一个遍历指针遍历整个数组&#xff0c;遇到红色与left所指位置数字交换&#xff0c;并将left自加…

鸿蒙开发实战-手写一个Openharmony投屏工具

实战手写一个Openharmony投屏工具&#xff0c;实现代码分享如下&#xff1a; java import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOExcepti…

一篇文章告诉你ELK Stack是什么

目录 ELK Stack简介 ELK Stack优点 ELK Stack组成 Elasticsearch Elasticsearch简介 Elasticsearch主要特点 Elasticsearch核心概念 Elasticsearch的配置 Logstash Logstash简介 Logstash过滤器之grok正则匹配 Logstash过滤器之mutate数据修改 Logstash过滤器之Ge…

如何快速将每个图片做二维码?批量生成图片码的步骤

现在很多商品的包装上扫码都会展现出物品的图片信息&#xff0c;每个物品都会有单独的一张物品信息图片。那么当导出一批图片后&#xff0c;如何快速将每张图片单独生成一个二维码来使用呢&#xff1f;本文小编将通过图文内容给大家讲解一下图片二维码生成器的批量建码功能该如…

automatic_mine_sweeper —— A project review to improve myself

1. How to understand the whole structure of the project? 1.Cbutton.h 和 Cbutton.cpp文件&#xff1a; Cbutton.h文件- // Cbutton.h : main header file for the CBUTTON application //#if !defined(AFX_CBUTTON_H__240DD99D_BEDE_49BD_A960_3268C3644816__INCLUDED_…

Python实用技巧:处理JSON文件写入换行问题

Python实用技巧&#xff1a;处理JSON文件写入换行问题 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程 &#x1f448; 希望得到您的订阅…
最新文章