QT实现简约美观的动画Checkbox

*最终效果:

*

一共三个文件: main.cpp , FancyCheckbox.h , FancyCheckbox.cpp

main.cpp

cpp 复制代码
#include <QApplication>
#include "FancyCheckbox.h"
#include <QGridLayout>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget* w = new QWidget;
    w->setWindowTitle("cool checkbox");
    w->resize(600,400);
    auto* layout = new QGridLayout;
    layout->addWidget(new CheckboxSimple,0,0);
    layout->addWidget(new CheckboxLollipop,0,1);
    layout->addWidget(new CheckboxNike,0,2);
    layout->addWidget(new CheckboxTransform,1,0);
    layout->addWidget(new CheckboxBanner,1,1);
    layout->addWidget(new CheckboxMark,1,2);
    w->setLayout(layout);
    w->show();
    return a.exec();
}

FancyCheckbox.h

cpp 复制代码
#ifndef CHECKBOXSIMPLE_H
#define CHECKBOXSIMPLE_H
#include <QPropertyAnimation>
#include <QWidget>
#include <QPaintEvent>
#include <QMouseEvent>

class CheckboxBase:public QWidget
{
    Q_OBJECT
    Q_PROPERTY(qreal angle READ angle WRITE setAngle)
public:
    CheckboxBase(QWidget* parent=nullptr);
    virtual ~CheckboxBase();

    qreal angle()const;
    void setAngle(qreal an);

protected:
    void enterEvent(QEvent* e);
    void leaveEvent(QEvent* e);
    QPropertyAnimation mAnim;
    qreal mAngle;
    bool mChecked;
};

class CheckboxSimple : public CheckboxBase
{
    Q_OBJECT
public:
    explicit CheckboxSimple(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent* e);
    void mouseReleaseEvent(QMouseEvent* e);
};
class CheckboxLollipop : public CheckboxBase
{
    Q_OBJECT
public:
    explicit CheckboxLollipop(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent* e);
    void mouseReleaseEvent(QMouseEvent* e);
};
class CheckboxNike: public CheckboxBase{
    Q_OBJECT
public:
    explicit CheckboxNike(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent* e);
    void mouseReleaseEvent(QMouseEvent* e);
};
class CheckboxTransform: public CheckboxBase{
    Q_OBJECT
public:
    explicit CheckboxTransform(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent* e);
    void mouseReleaseEvent(QMouseEvent* e);
};

class CheckboxBanner: public CheckboxBase{
    Q_OBJECT
public:
    explicit CheckboxBanner(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent* e);
    void mouseReleaseEvent(QMouseEvent* e);
};

class CheckboxMark: public CheckboxBase{
    Q_OBJECT
public:
    explicit CheckboxMark(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent* e);
    void mouseReleaseEvent(QMouseEvent* e);
};

#endif // CHECKBOXSIMPLE_H

FancyCheckbox.cpp

cpp 复制代码
#include "FancyCheckbox.h"
#include <QPainter>
#include <QPainterPath>
#include <QPalette>
#include <math.h>
CheckboxBase::CheckboxBase(QWidget* parent):
    QWidget(parent),mAngle(0),mChecked(false)
{
    mAnim.setPropertyName("angle");
    mAnim.setTargetObject(this);
    mAnim.setDuration(2000);
    mAnim.setLoopCount(1);
    mAnim.setEasingCurve(QEasingCurve::Linear);
    setMouseTracking(true);
}
CheckboxBase::~CheckboxBase(){}

qreal CheckboxBase::angle()const{ return mAngle;}
void CheckboxBase::setAngle(qreal an){
    mAngle = an;
    update();
}
void CheckboxBase::enterEvent(QEvent* e){
    setCursor(Qt::PointingHandCursor);
}
void CheckboxBase::leaveEvent(QEvent* e){
    setCursor(Qt::ArrowCursor);
}

CheckboxSimple::CheckboxSimple(QWidget *parent) : CheckboxBase(parent)
{
    setFixedSize(40,24);
    mAnim.setDuration(100);
    mAngle = 360;
}

void CheckboxSimple::paintEvent(QPaintEvent* e){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    int w = width();
    int h = height();
    painter.setPen(Qt::NoPen);
    if(mChecked){
        painter.setBrush(QBrush(QColor("lightgreen")));
    }
    else{
        painter.setBrush(QBrush(QColor("lightgray")));
    }
    painter.drawRoundedRect(rect(),h/2,h/2);
    painter.setBrush(QBrush(QColor("white")));
    const int gap = 3;
    if(mChecked){
        painter.translate((w-h)*mAngle/360+h/2,h/2);
    }
    else{
        painter.translate(w-h/2-(w-h)*mAngle/360 , h/2);
    }
    painter.drawEllipse(QPoint(0,0),h/2-gap,h/2-gap);
}
void CheckboxSimple::mouseReleaseEvent(QMouseEvent* e){
    if(mAnim.state() == QAbstractAnimation::Running) return;

    mChecked = !mChecked;
    mAnim.setStartValue(0);
    mAnim.setEndValue(360);
    mAnim.start();
}
CheckboxLollipop::CheckboxLollipop(QWidget *parent) : CheckboxBase(parent)
{
    setFixedSize(80,72);
    mAnim.setDuration(160);
    mAngle = 360;
}

void CheckboxLollipop::paintEvent(QPaintEvent* e){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    int w = width();
    int h = height();
    painter.setPen(Qt::NoPen);
    if(mChecked){
        painter.setBrush(QBrush(QColor("cornflowerblue")));
    }
    else{
        painter.setBrush(QBrush(QColor("gray")));
    }
    const auto r = 0.16 * h;
    const auto R = r + 0.25*w;

    painter.translate(0.25*w,0.4*h);

    if(mChecked){

        if(mAngle <= 180){//move to rightside
            painter.drawRoundedRect(QRectF(0,0,0.5*w,0.2*h),0.1*h,0.1*h);
            painter.setBrush(QBrush(QColor("blueviolet")));
            painter.translate((0.5*w - 2*r) * mAngle / 180 + r , 0.1*h);
            painter.drawEllipse(QPointF(0,0),r,r);
        }
        else{//boom!
            painter.translate(0.5*w - r,0.1*h);

            QRadialGradient rad(QPointF(0,0),R,QPointF(0,0));
            QColor base("blueviolet");
            base.setAlphaF(0.3);
            rad.setColorAt(0,base);
            rad.setColorAt(1,"transparent");

            QColor eraseColor("white");
            QWidget * pa = dynamic_cast<QWidget*>( parent() );
            if(pa){
                eraseColor = pa->palette().color(QPalette::Window);
            }
            painter.setBrush(rad);
            painter.drawEllipse(QPointF(0,0) , R , R);//画一个固定的渐变圈
            painter.setBrush(QBrush(eraseColor));
            auto out = r + (R -r) * ( mAngle - 180) / 180;
            painter.drawEllipse(QPointF(0,0), out,out);//画一个逐渐变大的圈把渐变圈覆盖掉

            painter.resetTransform();
            painter.translate(0.25*w,0.4*h);
            painter.setBrush(QBrush(QColor("cornflowerblue")));

            painter.drawRoundedRect(QRectF(0,0,0.5*w,0.2*h),0.1*h,0.1*h);
            painter.setBrush(QBrush(QColor("blueviolet")));

            painter.translate((0.5*w - r)  , 0.1*h);
            painter.drawEllipse(QPointF(0,0),r,r);
        }
    }
    else{
        painter.drawRoundedRect(QRectF(0,0,0.5*w,0.2*h),0.1*h,0.1*h);
        painter.setBrush(QBrush(QColor("white")));
        painter.translate(0.5*w-r-((0.5*w-2*r) * mAngle/360),0.1*h);
        painter.drawEllipse(QPointF(0,0),r,r);
    }

}
void CheckboxLollipop::mouseReleaseEvent(QMouseEvent* e){
    if(mAnim.state() == QAbstractAnimation::Running) return;
    mChecked = !mChecked;
    mAnim.setStartValue(0);
    mAnim.setEndValue(360);
    mAnim.start();
}

CheckboxNike::CheckboxNike(QWidget *parent){
    setFixedSize(32,32);
    mAngle = 360;
}

void CheckboxNike::paintEvent(QPaintEvent* e){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::NoPen);
    const int w = width();
    const int h = height();
    const int r = 8;
    if(mChecked){
        painter.setBrush(QBrush(QColor("lightblue")));
        painter.drawRoundedRect(rect() , r,r);
        QPainterPath cover;
        cover.addRect(QRectF(0,0,w*mAngle/360,h));
        painter.setClipPath(cover);
        painter.setPen(Qt::white);
        painter.setFont(QFont("Microsoft YaHei",12));
        painter.drawText(rect() , Qt::AlignCenter , "✔");
    }
    else{
        QColor baseBack("lightblue");
        QColor baseFront("white");
        baseBack.setAlphaF(1 - mAngle / 360);
        baseFront.setAlphaF(1 - mAngle / 360);
        painter.setBrush(baseBack);
        painter.drawRoundedRect(rect() , r,r);
        painter.setPen(baseFront);
        painter.setFont(QFont("Microsoft YaHei",12));
        painter.drawText(rect() , Qt::AlignCenter , "✔");

        QPen pen(QBrush("lightblue"),4);
        painter.setPen(pen);
        painter.setBrush(Qt::NoBrush);
        painter.drawRoundedRect(rect().adjusted(2,2,-2,-2) , r,r);
    }
}
void CheckboxNike::mouseReleaseEvent(QMouseEvent* e){
    if(mAnim.state() == QAbstractAnimation::Running) return;
    mChecked = !mChecked;
    mAnim.setStartValue(0);
    mAnim.setEndValue(360);
    if(mChecked){
        mAnim.setDuration(200);
    }
    else{
        mAnim.setDuration(100);
    }
    mAnim.start();
}

CheckboxTransform::CheckboxTransform(QWidget *parent){
    setFixedSize(40,40);
    mAngle = 360;
    mAnim.setDuration(200);
}

void CheckboxTransform::paintEvent(QPaintEvent* e){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    QPen pen("black");
    pen.setWidth(2);
    painter.setPen(pen);
    painter.setBrush(Qt::NoBrush);

    const double w = width();
    const double h = height();

    painter.translate(0.25*w,0.75*h);
    static constexpr int N = 20;
    if(mChecked){

        double theta = -M_PI_4 * mAngle / 360;//在数学中,逆时针角度是正,在计算机中,y轴朝下,逆时针的角度为负数
        double scale = 1 - (2-sqrt(2)/2)/2 / 360 * mAngle;
        for(int i = 0 ;i <= N;++i){
            double x = 0;
            double y = -h/2/N * i;
            auto x2 = scale * ( cos(theta) * x - sin(theta) * y);
            auto y2 = scale * ( sin(theta) * x + cos(theta) * y);
            painter.drawPoint(QPointF(x2,y2));
        }
        theta *= -1;
        for(int i = 0;i <= N;++i){
            double x = w/2/N*i;
            double y = 0;
            auto x2 = scale * ( cos(theta) * x - sin(theta) * y);
            auto y2 = scale * ( sin(theta) * x + cos(theta) * y);
            painter.drawPoint(QPointF(x2,y2));
        }
        painter.resetTransform();
        painter.translate(0.75*w, 0.25*h);
        theta = atan(2.0/3) * mAngle / 360;
        scale = 1- (2-sqrt(3.25))/2 * mAngle / 360;
        double dx = -0.125*w * mAngle / 360;
        double dy = 0.25*h * mAngle / 360;
        for(int i = 0;i <= N;i++){
            double x = 0;
            double y = 0.5*h/N * i;
            auto x2 = scale * ( cos(theta) * x - sin(theta) * y) + dx;
            auto y2 = scale * ( sin(theta) * x + cos(theta) * y) + dy;
            painter.drawPoint(QPointF(x2,y2));
        }
        theta = -atan(1.5) * mAngle / 360;
        dx *= -1;
        dy = -0.125*h * mAngle / 360;
        for(int i = 0;i <= N;i++){
            double x = -0.5*w * i / N;
            double y = 0;
            auto x2 = scale * ( cos(theta) * x - sin(theta) * y) + dx;
            auto y2 = scale * ( sin(theta) * x + cos(theta) * y) + dy;
            painter.drawPoint(QPointF(x2,y2));
        }


    }
    else{
        double theta = M_PI_4 * mAngle / 360;
        double scale = 1+ (2*sqrt(2) - 1) * mAngle / 360;
        for(int i = 0 ;i <= N;++i){
            double x = -0.125*w/N * i;
            double y = -0.125*h/N * i;
            auto x2 = scale * ( cos(theta) * x - sin(theta) * y);
            auto y2 = scale * ( sin(theta) * x + cos(theta) * y);
            painter.drawPoint(QPointF(x2,y2));
        }
        theta *= -1;
        for(int i = 0 ;i <= N;++i){
            double x = 0.125*w/N * i;
            double y = 0.125*h/N * i;
            auto x2 = scale * ( cos(theta) * x - sin(theta) * y);
            auto y2 = scale * ( sin(theta) * x + cos(theta) * y);
            painter.drawPoint(QPointF(x2,y2));
        }

        painter.resetTransform();
        painter.translate(0.625*w, 0.5*h);
        theta = -atan(2.0/3) * mAngle / 360;
        scale = 1 + (2.0/sqrt(3.25) - 1) * mAngle / 360;

        double dx = 0.125*w * mAngle / 360;
        double dy = -0.25*h * mAngle / 360;
        for(int i = 0;i <= N;i++){
            double x = -0.25*w * i/N;
            double y = 0.375*h * i / N;
            auto x2 = scale * ( cos(theta) * x - sin(theta) * y) + dx;
            auto y2 = scale * ( sin(theta) * x + cos(theta) * y) + dy;
            painter.drawPoint(QPointF(x2,y2));
        }
        theta = atan(1.5) * mAngle / 360;
        dx= -0.375*w * mAngle / 360;
        dy = -0.25*h * mAngle / 360;
        for(int i = 0;i <= N;i++){
            double x = 0.25*w * i / N;
            double y = -0.375*h * i / N;
            auto x2 = scale * ( cos(theta) * x - sin(theta) * y) + dx;
            auto y2 = scale * ( sin(theta) * x + cos(theta) * y) + dy;
            painter.drawPoint(QPointF(x2,y2));
        }
    }
}
void CheckboxTransform::mouseReleaseEvent(QMouseEvent* e){
    if(mAnim.state() == QAbstractAnimation::Running) return;
    mChecked = !mChecked;
    mAnim.setStartValue(0);
    mAnim.setEndValue(360);
    mAnim.start();
}

CheckboxBanner::CheckboxBanner(QWidget *parent){
    setFixedSize(75,30);
    mAnim.setDuration(300);
    mAngle = 360;
}

void CheckboxBanner::paintEvent(QPaintEvent* e){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::NoPen);
    const int w = width();
    const int h = height();

    if(mChecked){
        painter.setBrush(QBrush("lightgreen"));
    }
    else{
        painter.setBrush(QBrush("gray"));
    }
    QPainterPath pp;
    pp.moveTo(w/6,0);
    pp.lineTo(w,0);
    pp.lineTo(5.0/6*w,h);
    pp.lineTo(0,h);
    pp.lineTo(w/6,0);
    pp.closeSubpath();
    painter.drawPath(pp);

    QPen pen("white");
    pen.setWidth(4);
    painter.setPen(pen);
    painter.setFont(QFont("Microsoft YaHei",12));

    if(mChecked){//move to left
        painter.translate(w*(1 - sin(M_PI_2 * mAngle/360)),0);
        painter.drawText(QRect(0,0,w,h),Qt::AlignCenter, "ON");
    }
    else{
        painter.translate(w*(sin(M_PI_2 * mAngle/360) -1),0);
        painter.drawText(QRect(0,0,w,h),Qt::AlignCenter, "OFF");
    }
}
void CheckboxBanner::mouseReleaseEvent(QMouseEvent* e){
    if(mAnim.state() == QAbstractAnimation::Running) return;
    mChecked = !mChecked;
    mAnim.setStartValue(0);
    mAnim.setEndValue(360);
    mAnim.start();
}


CheckboxMark::CheckboxMark(QWidget *parent){
    setFixedSize(40,40);
    mAnim.setDuration(300);
    mAngle = 360;
}

void CheckboxMark::paintEvent(QPaintEvent* e){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(Qt::NoBrush);
    const int w = width();
    const int h = height();

    QPen pen;
    pen.setWidth(2);
    if(mChecked){
        pen.setColor("lightgreen");
    }
    else{
        pen.setColor("gray");
    }
    painter.setPen(pen);
    painter.translate(0,0.2*h);
    painter.drawRoundedRect(QRectF(0,0,0.8*w,0.8*h).adjusted(2,2,-2,-2),4,4);
    painter.resetTransform();
    painter.translate(0.125*w,0.5*h);

    if(mChecked){
        QPen pen("lightgreen");
        pen.setWidth(6);
        pen.setCapStyle(Qt::RoundCap);
        pen.setJoinStyle(Qt::RoundJoin);
        painter.setPen(pen);
        if(mAngle <= 120){
            int dw = 0.25*w * mAngle / 120;
            int dh = 0.25*h * mAngle / 120;
            painter.drawLine(0,0,dw,dh);
        }
        else{
            painter.drawLine(0,0,0.25*w,0.25*h);
            auto rat = (mAngle - 120) / 240.0;
            painter.resetTransform();
            painter.translate(0.375*w,0.75*h);
            int dw = 0.5*w * rat;
            int dh = 0.5*h * rat;
            painter.drawLine(0,0,dw,-dh);
        }
    }
    else{

    }

}
void CheckboxMark::mouseReleaseEvent(QMouseEvent* e){
    if(mAnim.state() == QAbstractAnimation::Running) return;
    mChecked = !mChecked;
    mAnim.setStartValue(0);
    mAnim.setEndValue(360);
    mAnim.start();
}
相关推荐
ks不知火4 分钟前
【仿muduo库one thread one loop式并发服务器实现】
linux·c++
mit6.82410 分钟前
[Lc(2)滑动窗口_1] 长度最小的数组 | 无重复字符的最长子串 | 最大连续1的个数 III | 将 x 减到 0 的最小操作数
数据结构·c++·算法·leetcode
zjoy_223312 分钟前
【数据结构】什么是栈||栈的经典应用||分治递归||斐波那契问题和归并算法||递归实现||顺序栈和链栈的区分
java·c语言·开发语言·数据结构·c++·算法·排序算法
局外人_Jia1 小时前
【简单的C++围棋游戏开发示例】
开发语言·c++·c·visual studio
加油,旭杏2 小时前
C++方向的面经
开发语言·c++
一只小小汤圆3 小时前
c++ std::tuple用法
开发语言·c++
柠石榴3 小时前
【练习】【二叉树】力扣热题100 102. 二叉树的层序遍历
c++·算法·leetcode·二叉树
越甲八千3 小时前
C++海康相机DEMO
开发语言·c++·数码相机
天若有情6734 小时前
用 C++ 实现选择题答案随机生成器:从生活灵感走向代码实践
c++·算法·生活
L73S374 小时前
C/C++输入输出(1)
c++·笔记·学习·考研·蓝桥杯