Qt 简约美观的加载动画 小沙漏风格 第六季

这次和大家分享一个沙漏风格的加载动画
效果如下:
在这里插入图片描述

这是本系列的第六季了, 本次内容的关键在于cubicTo函数的使用, 在这里分享一个非常好用的网站https://www.desmos.com/calculator/cahqdxeshd
在这上面可以手动拖动贝塞尔曲线的控制点, 并且显示了起终点和两个控制点的精确坐标, 这样来使用qt的cubicTo函数就非常方便了.

一共三个文件,可以直接编译运行

//main.cpp
#include "LoadingAnimWidget.h"
#include <QApplication>
#include <QGridLayout>
int main(int argc, char *argv[])
{QApplication a(argc, argv);QWidget w;w.setWindowTitle("加载动画 第6季");QGridLayout * mainLayout = new QGridLayout;auto* anim1= new FillGlassBead;mainLayout->addWidget(anim1,0,0);auto* anim2 = new FillGlassBead;anim2->setWaveType(FillGlassBead::WaveType::SwayingWater);mainLayout->addWidget(anim2,0,1);auto* anim3 = new Hourglass;mainLayout->addWidget(anim3,1,0);auto* anim4 = new Hourglass;anim4->setColumnVisibility(true);anim4->setSandColor("seagreen");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_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 FillGlassBead:public LoadingAnimBase{
public:FillGlassBead(QWidget* parent = nullptr);//一颗玻璃珠,内部逐渐充满液体enum class WaveType{PeacefulWater /*平静的水面*/ , SwayingWater /*左右晃动的水面*/};void setWaveType(WaveType t);
protected:void paintEvent(QPaintEvent*);
private:WaveType mWaveType;
};
class Hourglass:public LoadingAnimBase{
public:Hourglass(QWidget* parent = nullptr);//沙漏void setSandColor(const QColor& color);//设置沙子颜色void setColumnVisibility(bool vis);//设置柱子可见性
protected:void paintEvent(QPaintEvent*);
private:QColor mSandColor;bool mColumnVisible;
};
#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 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();
}
FillGlassBead::FillGlassBead(QWidget* parent):LoadingAnimBase (parent){mAnim.setDuration(3600);mWaveType = WaveType::PeacefulWater;
}
void FillGlassBead::setWaveType(WaveType t){if(mWaveType != t){mWaveType = t;update();}
}
void FillGlassBead::paintEvent(QPaintEvent* e){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);const int x = width();const int y = height();static const QColor color("lightseagreen");if(mAngle < 90){//只要画一个坠落的小球painter.translate(x/2,0);painter.setPen(Qt::NoPen);painter.setBrush(QBrush(color));qreal posY = y/4.0 * mAngle / 90;//坠落的小球最低点是高度的四分之一painter.drawEllipse(QPointF(0,posY),5,5);}else{painter.translate(x/2,0.625*y);QPen pen(color);pen.setWidth(4);painter.setPen(pen);painter.setBrush(Qt::NoBrush);const qreal r = 0.375*y;if(mAngle < 225){//画一个包裹玻璃珠的圆环//最高点起点角度是90,弧长是0,最低点起点角度是-90,弧长是360qreal proportion = (mAngle - 90) / 135.0;painter.drawArc(QRectF(-r,-r,2*r,2*r), 16*(90-180*proportion),16*360*proportion);}else{//画一个退去的包裹圆环//最开始起点角度是-90,最后起点角度是90qreal proportion = (mAngle - 225) / 135.0;painter.drawArc(QRectF(-r,-r,2*r,2*r),16*(-90+180*proportion),16*(360 - proportion*360));//再画一个上涨的水波QPainterPath pp;const qreal startx = -x/2;const qreal starty = 0.375*y - 0.75*y*proportion;if(mWaveType == WaveType::PeacefulWater){pp.addRect(QRectF(startx,starty,x,y));}else{QPointF start(startx,starty);QPointF end(start.x() + x,start.y());const qreal h = qSin(4*M_PI * proportion) *x * 0.3;QPointF c1( start.x() + 0.25*x, start.y() + h);QPointF c2( start.x() + 0.75*x, start.y() - h);pp.moveTo(start);pp.cubicTo(c1,c2,end);pp.lineTo(end.x(),999);//999没有具体含义,大一点就行了pp.lineTo(startx,999);pp.closeSubpath();}painter.setClipPath(pp);painter.setPen(Qt::NoPen);painter.setBrush(QBrush(color));const qreal r2 = r - 4;painter.drawEllipse(QRectF(-r2,-r2,2*r2,2*r2));}}
}
Hourglass::Hourglass(QWidget* parent):LoadingAnimBase (parent),mSandColor("lime"),mColumnVisible(false){ }
void Hourglass::setSandColor(const QColor& color){if(mSandColor != color){mSandColor = color;update();}
}
void Hourglass::setColumnVisibility(bool vis){if(vis != mColumnVisible){mColumnVisible = vis;update();}
}
void Hourglass::paintEvent(QPaintEvent*){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);const qreal x = width();const qreal y = height();//step1: 先画上下两个瓶盖qreal ang = (mAngle - 320)/40;//到320度的时候要旋转瓶子if(ang > 1) ang = 1;if(ang < 0) ang = 0;ang *= 180;QPen pen("lightslategray");//岩灰色的瓶身,就像我冰冷的内心pen.setWidth(16);pen.setCapStyle(Qt::RoundCap);painter.setBrush(Qt::NoBrush);painter.setPen(pen);painter.translate(x/2,y/2);painter.rotate(ang);painter.drawLine(-x/4,-0.375*y,x/4,-0.375*y);//上方瓶盖painter.drawLine(-x/4,0.375*y,x/4,0.375*y);//  下方瓶盖//step2: 画一个瓶身QPainterPath pp;const int gap1 = 8;//瓶身距离四分之一水平位置的间距,这个值越大,沙漏越瘦QPointF start(-x/4 + gap1 , -0.375*y);QPointF end(-x/32 , 0);QPointF c1(start.x(),end.y());QPointF c2(end.x(),end.y() + 0.4 * (start.y() - end.y()));pp.moveTo(start);pp.cubicTo(c1,c2,end);const qreal penWidth = 6;pen.setWidthF(penWidth);painter.setPen(pen);painter.drawPath(pp);//瓶身轮廓左上部分painter.rotate(180);painter.drawPath(pp);//右下painter.rotate(-180);painter.scale(1,-1);painter.drawPath(pp);//右上painter.rotate(180);painter.drawPath(pp);//左下//step3: 画两根小柱子(可选)if(mColumnVisible){pen.setWidthF(4);painter.setPen(pen);painter.drawLine(-x/4,start.y(),-x/4,-start.y());painter.drawLine(x/4,start.y(),x/4,-start.y());}painter.resetTransform();painter.translate(x/2,y/2);painter.setPen(Qt::NoPen);painter.setBrush(QBrush(mSandColor));if(mAngle < 320){ //step4: 画动态的沙子//画上面的沙子QPainterPath sand;start.setX(start.x() + penWidth/2);//沙子区域要瘦一点,免得盖住了瓶身end.setX(end.x() + penWidth/2);    //沙子区域要瘦一点,免得盖住了瓶身c1.setX(c1.x() + penWidth/2);      //沙子区域要瘦一点,免得盖住了瓶身c2.setX(c2.x() + penWidth/2);      //沙子区域要瘦一点,免得盖住了瓶身sand.moveTo(start);sand.cubicTo(c1,c2,end);sand.lineTo(0,(0.33-0.2*mAngle/320)*y);sand.lineTo(QPointF(end.x()*-1,end.y()));sand.cubicTo(QPointF(-c2.x(),c2.y()),QPointF(-c1.x(),c1.y()),QPointF(-start.x(),start.y()));sand.lineTo(start);painter.setClipPath(sand);painter.drawRect(QRectF(-x/2,-y/4+mAngle/320 * y *0.38,x,x));//画下面的沙子, 一个等腰三角形QPointF a(start.x() * mAngle/320,0.33*y);QPointF top(0,(0.33 - mAngle/320 * 0.2) * y);QPointF b(-a.x(),a.y());sand.moveTo(a);sand.lineTo(top);sand.lineTo(b);sand.closeSubpath();painter.setClipPath(sand);painter.drawRect(-x/2,top.y(),x,x);//这个高度不能太随意,否则会把上面的沙子也画出来}else{//旋转沙子QPainterPath pp;pp.moveTo(QPointF(start.x() + penWidth/2,0.33*y));pp.lineTo(0,0.13*y);pp.lineTo(QPointF(-start.x()-penWidth/2,0.33*y));pp.closeSubpath();QTransform bottoleTrans;bottoleTrans.rotate(ang);painter.setClipPath(bottoleTrans.map(pp));painter.drawRect(QRectF(-x/2,-x/2,x,x));}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端JS 时间复杂度和空间复杂度

时间复杂度 BigO 算法的时间复杂度通常用大 O 符号表述&#xff0c;定义为 T(n) O(f(n)) 实际就是计算当一个一个问题量级&#xff08;n&#xff09;增加的时候&#xff0c;时间T增加的一个趋势 T(n)&#xff1a;时间的复杂度&#xff0c;也就相当于所消耗的时长 O&#xff1…

动态规划(算法竞赛、蓝桥杯)--混合背包DP

1、B站视频链接&#xff1a;E14 背包DP 混合背包_哔哩哔哩_bilibili #include <bits/stdc.h> using namespace std; const int N1010,M10000; int a[M],b[M],c[M];//体积、价值、类型 int f[N];int main(){int n,m,v,w,s;cin>>n>>m;int num1;for(int i1;i&…

【Java程序设计】【C00331】基于Springboot的驾校预约学习系统(有论文)

基于Springboot的驾校预约学习系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的驾校预约学习系统&#xff0c;本系统有管理员、用户和教练三种角色&#xff1b; 管理员&#xff1a;个人中心、管理员管理、教练…

linux gdb 调试工具

1.写程序 首先&#xff0c;我们先写出一个 .c 或者.cpp程序 如 然后 gcc -g hello.c -o hello 或者 g -g hello.cpp -o hello &#xff08;-g&#xff09;要加 2. gdb调试 用 gdb &#xff08;可执行程序&#xff0c;如hello&#xff09; 进入之后&#xff0c;有…

mongoDB 优化(1)索引

1、创建复合索引&#xff08;多字段&#xff09; db.collection_test1.createIndex({deletedVersion: 1,param: 1,qrYearMonth: 1},{name: "deletedVersion_1_param_1_qrYearMonth_1",background: true} ); 2、新增索引前&#xff1a; 执行查询&#xff1a; mb.r…

[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…

mini-spring|关于Bean对象作用域以及FactoryBean的实现和使用

需求 FactoryBean 直接配置FactoryBean 获取FactoryBean中的Bean对象 FactoryBean的getObject方法通过反射获取Bean对象 由此省去对实体Dao类的定义 解决方法 对外提供一个可以二次从 FactoryBean 的 getObject 方法中获取对象的功能即可 整体架构 整个的实现过程包括了两部…

Python编程实验五:文件的读写操作

目录 一、实验目的与要求 二、实验内容 三、主要程序清单和程序运行结果 第1题 第2题 四、实验结果分析与体会 一、实验目的与要求 &#xff08;1&#xff09;通过本次实验&#xff0c;学生应掌握与文件打开、关闭相关的函数&#xff0c;以及与读写操作相关的常用方法的…

第五课:BIO高级操作

一、BIO模式下的端口转发思想 需求&#xff1a;需要实现是个客户端的消息可以发送给所有的客户端接收.(群聊实现) 我们每一个客户端都会和服务器端建立一个socket通道&#xff0c;这样服务器端和客户端就能够实现通信。为了让服务器端知道我们当前有多少个socket正在运行&…

国际黄金价格是什么?和黄金价格有何区别?

黄金是世界上最珍贵的贵金属之一&#xff0c;其价值被无数人所垂涎。而国际黄金价格作为市场上的参考指标&#xff0c;直接影响着黄金交易的买卖。那么国际黄金价格到底是什么&#xff0c;与黄金价格又有何区别呢&#xff1f;本文将为您详细解答。 国际黄金价格是指以美元计量的…

如何恢复数据?5个实用数据恢复方法!

亲爱的朋友们&#xff0c;你是否也有过这样的经历&#xff0c;电脑里的数据突然消失&#xff0c;让你手足无措&#xff1f;别担心&#xff0c;今天我就来教你如何恢复数据&#xff0c;让你不再为数据丢失而烦恼。 首先&#xff0c;我们需要了解数据丢失的原因。可能是你不小心…

ZDH-大数据采集-支持KETTLE任务

目录 目录 项目源码 预览地址 支持KETTLE介绍 新增KETTLE任务 配置调度KETTLE 重要说明 感谢支持 项目源码 预览地址 支持KETTLE介绍 新增KETTLE任务 配置调度KETTLE 重要说明 项目源码 zdh_web:GitHub - zhaoyachao/zdh_web: 大数据采集,抽取平台 预览地址 后…