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_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 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 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();
}
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,弧长是360
            qreal proportion = (mAngle - 90) / 135.0;
            painter.drawArc(QRectF(-r,-r,2*r,2*r), 16*(90-180*proportion),16*360*proportion);
        }
        else{
            //画一个退去的包裹圆环
            //最开始起点角度是-90,最后起点角度是90
            qreal 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));
    }
}

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

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

相关文章

深入剖析k8s-Pod篇

为什么需要Pod&#xff1f; 进程是以进程组的方式组织在一起。受限制容器的“单进程模型”&#xff0c; 成组调用没有被妥善处理&#xff08;资源调用有限&#xff09;&#xff0c;使用资源囤积则导致复杂度上升。 在k8s项目中&#xff0c;Pod的实现需要使用一个中间容器——…

兰州理工大学数据科学与计算机科学20级学生实训项目正式开班

2月26日星期一&#xff0c;兰州理工大学数据科学与计算机科学20级学生实训项目正式开班。计通学院计算机科学与大数据专业相关领导及老师、泰迪智能科技华北区域负责人曹玉红参与本次开班仪式。 兰州理工大学计算机与通信学院&#xff08;软件学院&#xff09;肇始于1984年原甘…

Unity 游戏设计模式:单例模式

本文由 简悦 SimpRead 转码&#xff0c; 原文地址 mp.weixin.qq.com 单例模式 在 C# 游戏设计中&#xff0c;单例模式是一种常见的设计模式&#xff0c;它的主要目的是确保一个类只有一个实例&#xff0c;并提供一个全局访问点。单例模式在游戏开发中具有以下几个作用&#xf…

实例驱动计算机网络

文章目录 计算机网络的层次结构应用层DNSHTTP协议HTTP请求响应过程 运输层TCP协议TCP协议面向连接实现TCP的三次握手连接TCP的四次挥手断开连接 TCP协议可靠性实现TCP的流量控制TCP的拥塞控制TCP的重传机制 UDP协议 网际层IP协议&#xff08;主机与主机&#xff09;IP地址的分类…

软考53-上午题-【数据库】-关系模式的范式

一、范式 关系模式的规范化标准&#xff0c;达到范式的关系才是规范化的。 1-1、目前有6种范式&#xff1a; 第一范式&#xff1a;1NF&#xff1b;&#xff08;满足最低要求&#xff09;第二范式&#xff1a;2NF&#xff1b;&#xff08;在第一范式的基础上进一步的满足一些要…

2024最新版聚合支付彩虹易支付PHP源码

彩虹易支付是一种便捷的支付解决方案&#xff0c;属于聚合易支付平台的一部分。它提供了即时到账功能&#xff0c;无需签约即可使用。通过这个平台&#xff0c;您可以方便地接入多种支付方式&#xff0c;包括支付宝当面付、QQ钱包、财付通、微信扫码支付和个体商户聚合收款码等…

2024尼泊尔徒步旅行记

2.8日至2.24日这段时间去了尼泊尔徒步旅行&#xff0c;是的今年没有在家过春节。 加德满都 当我到了尼泊尔的首都加德满都&#xff0c;下飞机了坐在一辆狭小的出租车上&#xff0c;行驶在狭窄的路上。看到的是路上很多人&#xff0c;有很多摩托车&#xff0c;没有红绿灯&…

剑指 Offer 42. 连续子数组的最大和

目录 题目 代码实现 输出 题目 代码实现 #include <vector> #include <iostream> using namespace std;class Solution { public:int maxSubArray(vector<int>& nums) {vector<int> sum;sum.resize(nums.size() + 1);for (size_t i = 1; i <…

UCSF DOCK 分子对接详细案例(01)- rigid, fixed anchor, flexible dock

欢迎浏览我的CSND博客&#xff01; Blockbuater_drug …点击进入 文章目录 前言一、操作环境二、研究背景三、受体-配体结构文件准备3.1准备文件夹DOCK_workdir, 下载晶体结构3.1.1 来自湿实验的受体配体共晶结构&#xff1a;3.1.2 来自深度学习和语言模型推理预测的蛋白结构&a…

用电商API,轻松获取淘宝商品数据!

我们进入到淘宝商品数据页面&#xff0c;按F12打开开发者模式&#xff0c;对页面进行观察&#xff0c;我们发现淘宝页面是Ajax方式加载的&#xff0c;而且它的接口参数很复杂且没有固定的规律&#xff0c;但是Selenium又被淘宝反爬限制了&#xff0c;所以我们不能使用Ajax来获取…

vue2 设置keepAlive之后怎么刷新页面数据

场景&#xff1a;移动端有 A、B、C 三个页面&#xff0c;A、B 页面路由设置了keepAlive属性&#xff0c;有下面两个场景&#xff1a; 1、A 页面 --> B 页面&#xff0c;B 页面刷新。 2、C 页面 --> B页面&#xff0c;B 页面不刷新。 一、分为以下两个情况讨论&#xf…

cAdvisor+Prometheus+Grafana 搞定Docker容器监控平台

cAdvisorPrometheusGrafana cAdvisorPrometheusGrafana 搞定Docker容器监控平台1、先给虚拟机上传cadvisor2、What is Prometheus?2.1、架构图 3、利用docker安装普罗米修斯4、安装grafana cAdvisorPrometheusGrafana 搞定Docker容器监控平台 1、先给虚拟机上传cadvisor cAd…

fastjson反序列化漏洞

fastjson反序列化漏洞 文章目录 fastjson反序列化漏洞1.漏洞原理2.探测方式2.1 查看回显2.2 LDAP/RMI服务测试 3.LDAP/RMI服务搭建要求4.漏洞复现4.1 fastjson <1.2.47 反序列化导致任意命令执行漏洞4.1.1 环境准备4.1.2 复现过程 4.2 fastjson <1.2.24 反序列化导致任意…

OpenHarmony、HarmonyOS打开编辑 PDF 等操作的三方组件使用教程

项目场景: 随着数字化时代的发展,PDF 文档成为广泛应用于各行业的重要文件格式。为了提高OpenHarmony/HarmonyOS生态系统的功能性和用户体验,我们需要一款支持打开、编辑PDF文件的应用程序。 使用户能够轻松打开、浏览和编辑PDF文件。该应用将充分利用OpenHarmony/HarmonyO…

激光雷达原理

全球汽车行业正在进行自动化变革&#xff0c;这将彻底改变交通运输的安全和效率水平。 戴姆勒在S级豪华车型中引入L3级自动驾驶&#xff08;L3&#xff0c;在特定条件下自动驾驶&#xff0c;人类驾驶员一旦被请求就会随时接管&#xff09;是自动驾驶革命的一个重大突破。其他多…

Zoho Projects 8.0震撼来袭,项目管理再升级! 全方位功能变化解读

有效的项目管理方法保证项目按照进度、成本、质量要求进行交付&#xff0c;是针对单个项目或项目群的管理&#xff0c;从而确保项目符合企业的战略目标&#xff0c;实现企业收益最大化。 对于项目管理工作来说&#xff0c;我们通常会认为只有专业的经理才能胜任&#xff0c;软件…

k8s中容器的调度与创建:CRI,cgroup

container调度与创建 选自&#xff1a;K8s、CRI与container - packy的文章 - 知乎 https://zhuanlan.zhihu.com/p/102897620 Cgroup创建&#xff1a; cgexec -g cpu,memory:$UUID \ > unshare -uinpUrf --mount-proc \ > sh -c "/bin/hostname $UUID &…

力扣hot100题解(python版36-40题)

36、二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 *中序 遍历* 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&am…

四、西瓜书——支持向量机

第六章 支持向量机 1.间隔与支持向量 支持向量机的原理是寻找与支持向量具有最大间隔的划分超平面。支持向量机具有一个重要性质: 训练完成后,大部分的训练样本都不需保留,最终模型仅与支持向量有关. 首先&#xff0c;超平面的方程为&#xff1a; 点到超平面的距离为&#xff…

如何通过Jenkins进行自动化构建项目

1. 介绍 Jenkins 是一个开源的持续集成&#xff08;CI&#xff09;和持续交付&#xff08;CD&#xff09;工具&#xff0c;旨在自动化软件开发过程中的构建、测试和部署。它是一个基于 Java 编写的自动化服务器&#xff0c;在软件开发生命周期的各个阶段提供自动化支持。 官方…