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

  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值