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

这次和大家分享一个沙漏风格的加载动画
效果如下:
Qt 简约美观的加载动画 小沙漏风格 第六季_第1张图片

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

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

//main.cpp
#include "LoadingAnimWidget.h"
#include 
#include 
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 
#include 
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 
#include 
#include 
#include 
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));
    }
}

你可能感兴趣的:(QT,qt,数据库,开发语言)