1.界面实现效果
以下是具体的项目需要用到的效果展示,可以根据需要,实例化想要的按钮。
2.简介
原理:使用Qt的QPropertyAnimation动画类,这里简单来说就是切换两个按钮样式。
请看以下结构体:
cpp
#define MAX_LINE_COUNT 3
struct PurelinStatus
{
QSizeF bgSize; // 背景尺寸
double bgRadius = 0; // 背景圆角
QColor bgColor = Qt::white; // 背景颜色
int useLine = 0; // 使用的线条数量
QLineF linePoss[MAX_LINE_COUNT]; // 各线条位置
QPointF lineHide[MAX_LINE_COUNT]; // 默认消失的位置
QColor lineColors[MAX_LINE_COUNT]; // 各线条颜色
PurelinStatus(int useLine = 0) : useLine(useLine)
{
for (int i = useLine; i < MAX_LINE_COUNT; i++)
{
linePoss[i] = QLineF(0, 0, 0, 0);
lineColors[i] = Qt::transparent;
}
}
};
根据线条的数量、颜色、背景颜色、线条消失的位置等属性,在paintEvent中进行绘制图形。
cpp
void PurelinButton::paintEvent(QPaintEvent *)
{
// 背景位置
QSizeF bgSize = currentStatus.bgSize;
if (bgSize.isEmpty())
bgSize = this->size();
double left = (this->width() - bgSize.width()) / 2;
double top = (this->height() - bgSize.height()) / 2;
QRectF rect(left, top, bgSize.width(), bgSize.height());
// 绘制背景
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
QPainterPath path;
path.addRoundedRect(rect, currentStatus.bgRadius, currentStatus.bgRadius);
painter.fillPath(path, currentStatus.bgColor);
// 绘制前景
const double penWidth = 3.0;
for (int i = 0; i < currentStatus.useLine; i++)
{
const QLineF& line = currentStatus.linePoss[i];
if ((line.length() < 1e-4))
continue;
painter.setPen(QPen(currentStatus.lineColors[i], penWidth, Qt::SolidLine, Qt::RoundCap));
painter.drawLine(currentStatus.linePoss[i]);
}
}
3.Button类实现
cpp
#ifndef PURELINBUTTON_H
#define PURELINBUTTON_H
#include <QPushButton>
#include <QList>
#define MAX_LINE_COUNT 3
struct PurelinStatus
{
QSizeF bgSize; // 背景尺寸
double bgRadius = 0; // 背景圆角
QColor bgColor = Qt::white; // 背景颜色
int useLine = 0; // 使用的线条数量
QLineF linePoss[MAX_LINE_COUNT]; // 各线条位置
QPointF lineHide[MAX_LINE_COUNT]; // 默认消失的位置
QColor lineColors[MAX_LINE_COUNT]; // 各线条颜色
PurelinStatus(int useLine = 0) : useLine(useLine)
{
for (int i = useLine; i < MAX_LINE_COUNT; i++)
{
linePoss[i] = QLineF(0, 0, 0, 0);
lineColors[i] = Qt::transparent;
}
}
};
class PurelinButton : public QPushButton
{
Q_OBJECT
public:
PurelinButton(QWidget* parent = nullptr);
const PurelinStatus& getCurrentStatus() const;
void setCurrentStatus(PurelinStatus status);
/// 加载显示状态
void load(PurelinStatus status);
protected:
void paintEvent(QPaintEvent *) override;
private slots:
virtual void aniProgChanged(const QVariant& var);
void aniProgFinished();
private:
bool animating = false;
double animationProg = 0;
PurelinStatus currentStatus;
PurelinStatus prevStatus;
PurelinStatus nextStatus;
};
#endif // PURELINBUTTON_H
#include "purelinbutton.h"
#include <QPainter>
#include <QPainterPath>
#include <QDebug>
#include <QPropertyAnimation>
PurelinButton::PurelinButton(QWidget *parent) : QPushButton(parent)
{
}
const PurelinStatus &PurelinButton::getCurrentStatus() const
{
return currentStatus;
}
void PurelinButton::setCurrentStatus(PurelinStatus status)
{
this->currentStatus = status;
}
void PurelinButton::load(PurelinStatus status)
{
prevStatus = currentStatus;
nextStatus = status;
if (prevStatus.useLine < nextStatus.useLine)
{
for (int i = prevStatus.useLine; i < nextStatus.useLine; i++)
{
prevStatus.linePoss[i] = QLineF(nextStatus.lineHide[i], nextStatus.lineHide[i]);
prevStatus.lineColors[i] = nextStatus.lineColors[i];
}
}
else
{
for (int i = nextStatus.useLine; i < prevStatus.useLine; i++)
{
nextStatus.linePoss[i] = QLineF(prevStatus.lineHide[i], prevStatus.lineHide[i]);
nextStatus.lineColors[i] = prevStatus.lineColors[i];
}
}
if (prevStatus.bgSize.isEmpty())
prevStatus.bgSize = this->size();
if (nextStatus.bgSize.isEmpty())
nextStatus.bgSize = this->size();
QPropertyAnimation* ani = new QPropertyAnimation(this, "");
ani->setStartValue(0.0);
ani->setEndValue(1.0);
ani->setDuration(500);
ani->setEasingCurve(QEasingCurve::OutQuad);
connect(ani, &QPropertyAnimation::valueChanged, this, &PurelinButton::aniProgChanged);
connect(ani, SIGNAL(finished()), ani, SLOT(deleteLater()));
connect(ani, SIGNAL(finished()), this, SLOT(aniProgFinished()));
ani->start();
}
void PurelinButton::paintEvent(QPaintEvent *)
{
// 背景位置
QSizeF bgSize = currentStatus.bgSize;
if (bgSize.isEmpty())
bgSize = this->size();
double left = (this->width() - bgSize.width()) / 2;
double top = (this->height() - bgSize.height()) / 2;
QRectF rect(left, top, bgSize.width(), bgSize.height());
// 绘制背景
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
QPainterPath path;
path.addRoundedRect(rect, currentStatus.bgRadius, currentStatus.bgRadius);
painter.fillPath(path, currentStatus.bgColor);
// 绘制前景
const double penWidth = 3.0;
for (int i = 0; i < currentStatus.useLine; i++)
{
const QLineF& line = currentStatus.linePoss[i];
if ((line.length() < 1e-4))
continue;
painter.setPen(QPen(currentStatus.lineColors[i], penWidth, Qt::SolidLine, Qt::RoundCap));
painter.drawLine(currentStatus.linePoss[i]);
}
}
void PurelinButton::aniProgChanged(const QVariant &var)
{
// 可以重新这个方法,自定义尺寸
// 比如,按照控件的百分比大小进行调整,而不是像素数值
double prog = var.toDouble();
currentStatus.bgRadius = prevStatus.bgRadius + prog * (nextStatus.bgRadius - prevStatus.bgRadius);
currentStatus.bgSize = QSizeF(prevStatus.bgSize.width() + prog * (nextStatus.bgSize.width() - prevStatus.bgSize.width()),
prevStatus.bgSize.height() + prog * (nextStatus.bgSize.height() - prevStatus.bgSize.height()));
currentStatus.bgColor.setRgbF(
prevStatus.bgColor.redF() + prog * (nextStatus.bgColor.redF() - prevStatus.bgColor.redF()),
prevStatus.bgColor.greenF() + prog * (nextStatus.bgColor.greenF() - prevStatus.bgColor.greenF()),
prevStatus.bgColor.blueF() + prog * (nextStatus.bgColor.blueF() - prevStatus.bgColor.blueF()),
prevStatus.bgColor.alphaF() + prog * (nextStatus.bgColor.alphaF() - prevStatus.bgColor.alphaF()));
int lineCount = qMax(prevStatus.useLine, nextStatus.useLine);
currentStatus.useLine = lineCount;
for (int i = 0; i < lineCount; i++)
{
currentStatus.linePoss[i].setLine(
prevStatus.linePoss[i].x1() + prog * (nextStatus.linePoss[i].x1() - prevStatus.linePoss[i].x1()),
prevStatus.linePoss[i].y1() + prog * (nextStatus.linePoss[i].y1() - prevStatus.linePoss[i].y1()),
prevStatus.linePoss[i].x2() + prog * (nextStatus.linePoss[i].x2() - prevStatus.linePoss[i].x2()),
prevStatus.linePoss[i].y2() + prog * (nextStatus.linePoss[i].y2() - prevStatus.linePoss[i].y2()));
currentStatus.lineColors[i].setRgbF(
prevStatus.lineColors[i].redF() + prog * (nextStatus.lineColors[i].redF() - prevStatus.lineColors[i].redF()),
prevStatus.lineColors[i].greenF() + prog * (nextStatus.lineColors[i].greenF() - prevStatus.lineColors[i].greenF()),
prevStatus.lineColors[i].blueF() + prog * (nextStatus.lineColors[i].blueF() - prevStatus.lineColors[i].blueF()),
prevStatus.lineColors[i].alphaF() + prog * (nextStatus.lineColors[i].alphaF() - prevStatus.lineColors[i].alphaF()));
}
update();
}
void PurelinButton::aniProgFinished()
{
currentStatus = nextStatus;
animating = false;
}
4.使用
MainWindow界面上拖动一个QPushButton按钮,将这个按钮提升为我们自定义的类。
cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QGraphicsDropShadowEffect>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this);
shadow->setOffset(3, 5);
shadow->setColor(QColor("#44888888"));
shadow->setBlurRadius(20);
ui->pushButton->setGraphicsEffect(shadow);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
const int totalCount = 8;
static int index = -1;
int dx = 0;
// -
PurelinStatus status[totalCount];
int i = 0;
status[i].bgSize = QSize(100, 60);
status[i].bgColor = Qt::white;
status[i].bgRadius = 30;
status[i].useLine = 1;
status[i].linePoss[0] = QLineF(35, 50, 65, 50);
status[i].lineHide[0] = QPointF(50, 50);
status[i].lineColors[0] = QColor("#ed657d");
// +
i++;
status[i].bgSize = QSize(-1, -1);
status[i].bgColor = Qt::white;
status[i].bgRadius = 30;
status[i].useLine = 2;
status[i].linePoss[0] = QLineF(35, 50, 65, 50);
status[i].linePoss[1] = QLineF(50, 65, 50, 35);
status[i].lineHide[0] = QPointF(50, 50);
status[i].lineHide[1] = QPointF(50, 50);
status[i].lineColors[0] = QColor("#5baaf8");
status[i].lineColors[1] = QColor("#5baaf8");
// x
i++;
status[i].bgSize = QSize(-1, -1);
status[i].bgColor = Qt::white;
status[i].bgRadius = 50;
status[i].useLine = 2;
status[i].linePoss[0] = QLineF(40, 40, 60, 60);
status[i].linePoss[1] = QLineF(40, 60, 60, 40);
status[i].lineHide[0] = QPointF(50, 50);
status[i].lineHide[1] = QPointF(50, 50);
status[i].lineColors[0] = QColor("#b9b9b9");
status[i].lineColors[1] = QColor("#b9b9b9");
// √
i++;
dx = -2;
status[i].bgSize = QSize(-1, -1);
status[i].bgColor = Qt::white;
status[i].bgRadius = 50;
status[i].useLine = 2;
status[i].linePoss[0] = QLineF(40 + dx, 50, 50 + dx, 60);
status[i].linePoss[1] = QLineF(50 + dx, 60, 70 + dx, 40);
status[i].lineHide[0] = QPointF(50 + dx, 50);
status[i].lineHide[1] = QPointF(50 + dx, 50);
status[i].lineColors[0] = QColor("#59ce84");
status[i].lineColors[1] = QColor("#59ce84");
// <
i++;
dx = -4;
status[i].bgSize = QSize(-1, -1);
status[i].bgColor = Qt::white;
status[i].bgRadius = 50;
status[i].useLine = 2;
status[i].linePoss[0] = QLineF(42 + dx, 50, 58 + dx, 65);
status[i].linePoss[1] = QLineF(42 + dx, 50, 58 + dx, 35);
status[i].lineHide[0] = QPointF(50, 50);
status[i].lineHide[1] = QPointF(50, 50);
status[i].lineColors[0] = QColor("#4b6fea");
status[i].lineColors[1] = QColor("#4b6fea");
// =
i++;
status[i].bgSize = QSize(100, 60);
status[i].bgColor = Qt::white;
status[i].bgRadius = 30;
status[i].useLine = 2;
status[i].linePoss[0] = QLineF(35, 55, 65, 55);
status[i].linePoss[1] = QLineF(35, 45, 65, 45);
status[i].lineHide[0] = QPointF(50, 55);
status[i].lineHide[1] = QPointF(50, 45);
status[i].lineColors[0] = QColor("#eda244");
status[i].lineColors[1] = QColor("#eda244");
// 三
i++;
status[i].bgSize = QSize(-1, -1);
status[i].bgColor = Qt::white;
status[i].bgRadius = 20;
status[i].useLine = 3;
status[i].linePoss[1] = QLineF(34, 37, 66, 37);
status[i].linePoss[0] = QLineF(40, 50, 60, 50);
status[i].linePoss[2] = QLineF(46, 63, 54, 63);
status[i].lineHide[1] = QPointF(50, 37);
status[i].lineHide[0] = QPointF(50, 50);
status[i].lineHide[2] = QPointF(50, 63);
status[i].lineColors[1] = QColor("#7248e3");
status[i].lineColors[0] = QColor("#7248e3");
status[i].lineColors[2] = QColor("#7248e3");
// ≡
i++;
status[i].bgSize = QSize(-1, -1);
status[i].bgColor = Qt::white;
status[i].bgRadius = 20;
status[i].useLine = 3;
status[i].linePoss[1] = QLineF(35, 37, 65, 37);
status[i].linePoss[0] = QLineF(35, 50, 55, 50);
status[i].linePoss[2] = QLineF(35, 63, 60, 63);
status[i].lineHide[1] = QPointF(35, 37);
status[i].lineHide[0] = QPointF(35, 50);
status[i].lineHide[2] = QPointF(35, 63);
status[i].lineColors[1] = QColor("#424649");
status[i].lineColors[0] = QColor("#424649");
status[i].lineColors[2] = QColor("#424649");
if (++index >= totalCount)
index = 0;
ui->pushButton->load(status[index]);
}