多谢飞扬青春的博客,看到水波形进度条的效果觉得非常不错,于是自己也模仿着做了一个类似的,效果图:
原理:
利用正弦曲线产生平滑曲线点集合,然后用大路径减去当前进度路径,形成水波效果。
源码如下:
#ifndef SPROGRESSBAR_H
#define SPROGRESSBAR_H
#include
#include
#include
class SProgressBar : public QWidget
{
Q_OBJECT
public:
explicit SProgressBar(QWidget *parent = 0);
enum PercentStyle_Type{
PercentStyle_Rect = 0,//矩形
PercentStyle_Circle,//圆
PercentStyle_Ellipse,//椭圆
};
void setValue(int value) {m_value = value;}
int value() {return m_value;}
void setMaxValue(int value) {m_maxValue = value;}
int maxValue() {return m_maxValue;}
void setMinValue(int value) {m_minValue = value;}
int minValue() {return m_minValue;}
void setPercentStyle(SProgressBar::PercentStyle_Type type) {m_percentStyle = type;}
PercentStyle_Type percentStyle() {return m_percentStyle;}
void setWaterDensity(int val) {m_waterDensity = val;}
int waterDensity() {return m_waterDensity;}
void setColor(QColor col) {m_usedColor = col;}
QColor color() {return m_usedColor;}
void setWaterHeight(double val) {m_waterHeight = val;}
double waterHeight() {return m_waterHeight;}
void setBorderWidth(int val) {m_borderWidth = val;}
int borderWidth() {return m_borderWidth;}
void setTextColor(QColor col) {m_textColor = col;}
QColor textColor() {return m_textColor;}
void setBoderColor(QColor col) {m_boderColor = col;}
QColor boderColor() {return m_boderColor;}
void setBgColor(QColor col) {m_bgColor = col;}
QColor bgColor() {return m_bgColor;}
protected:
void paintEvent(QPaintEvent *event);
void timerEvent(QTimerEvent *event);
private:
PercentStyle_Type m_percentStyle = PercentStyle_Rect;
QColor m_usedColor = QColor(180,255,255);
QColor m_textColor = Qt::white;
QColor m_boderColor = Qt::black;
QColor m_bgColor = Qt::gray;
QFont m_font;
int m_value = 80;
int m_minValue = 0;
int m_maxValue = 100;
int m_waterDensity = 10; // 水波的密度
double m_waterHeight = 0.03;
double m_offset = 50;
int m_borderWidth = 10;
};
#endif // SPROGRESSBAR_H
#include "sprogressbar.h"
SProgressBar::SProgressBar(QWidget *parent) : QWidget(parent)
{
m_font.setFamily("Microsoft YaHei");
this->startTimer(80);
}
void SProgressBar::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing); // 反锯齿;
int height = this->height();
int width = this->width();
int side = qMin(width, height);
//计算当前值所占百分比
double percent = 1 - (double)(m_value - m_minValue) / (m_maxValue - m_minValue);
//正弦曲线公式 y = A * sin(ωx + φ) + k
//w表示周期,可以理解为水波的密度,值越大密度越大(浪越密集 ^_^),取值 密度*M_PI/宽度
double w = m_waterDensity * M_PI / width;
//A表示振幅,可以理解为水波的高度,值越大高度越高(越浪 ^_^),取值高度的百分比
double A = height * m_waterHeight;
//k表示y轴偏移,可以理解为进度,取值高度的进度百分比
double k = height * percent;
//第一条波浪路径集合
QPainterPath waterPath1;
//第二条波浪路径集合
QPainterPath waterPath2;
//移动到左上角起始点
waterPath1.moveTo(0, height);
waterPath2.moveTo(0, height);
m_offset += 0.6;
if (m_offset > (width / 2)) {
m_offset = 0;
}
for(int x = 0; x <= width; x++) {
//第一条波浪Y轴
double waterY1 = (double)(A * sin(w * x + m_offset)) + k;
//第二条波浪Y轴
double waterY2 = (double)(A * sin(w * x + m_offset + (width / 2 * w))) + k;
//如果当前值为最小值则Y轴为高度
if (m_value == m_minValue) {
waterY1 = height;
waterY2 = height;
}
//如果当前值为最大值则Y轴为0
if (m_value == m_maxValue) {
waterY1 = 0;
waterY2 = 0;
}
waterPath1.lineTo(x, waterY1);
waterPath2.lineTo(x, waterY2);
}
//移动到右下角结束点,整体形成一个闭合路径
waterPath1.lineTo(width, height);
waterPath2.lineTo(width, height);
//大路径
QPainterPath bigPath;
if (m_percentStyle == PercentStyle_Rect) {
width = width - m_borderWidth * 2;
height = height - m_borderWidth * 2;
bigPath.addRect(m_borderWidth, m_borderWidth, width, height);
painter.setBrush(m_boderColor);
painter.drawRect(this->rect());
painter.setBrush(m_bgColor);
painter.drawRect(m_borderWidth, m_borderWidth, width, height);
} else if (m_percentStyle == PercentStyle_Circle) {
painter.setBrush(m_boderColor);
painter.drawEllipse((width - side) / 2, (height - side) / 2, side, height);
side = side - m_borderWidth * 2;
bigPath.addEllipse((width - side) / 2, m_borderWidth, side, side);
painter.setBrush(m_bgColor);
painter.drawEllipse((width - side) / 2, m_borderWidth, side, side);
} else if (m_percentStyle == PercentStyle_Ellipse) {
width = width - m_borderWidth * 2;
height = height - m_borderWidth * 2;
bigPath.addEllipse(m_borderWidth, m_borderWidth, width, height);
painter.setBrush(m_boderColor);
painter.drawEllipse(this->rect());
painter.setBrush(m_bgColor);
painter.drawEllipse(m_borderWidth, m_borderWidth, width, height);
}
painter.save();
//新路径,用大路径减去波浪区域的路径,形成遮罩效果
QPainterPath path;
painter.setPen(Qt::NoPen);
QColor waterColor1 = m_usedColor;
waterColor1.setAlpha(100);
QColor waterColor2 = m_usedColor;
waterColor2.setAlpha(180);
//第一条波浪挖去后的路径
path = bigPath.intersected(waterPath1);
painter.setBrush(waterColor1);
painter.drawPath(path);
//第二条波浪挖去后的路径
path = bigPath.intersected(waterPath2);
painter.setBrush(waterColor2);
painter.drawPath(path);
painter.restore();
//绘制文字
m_font.setPixelSize(this->width()/4);
painter.setPen(m_textColor);
painter.setFont(m_font);
painter.drawText(this->rect(), Qt::AlignCenter, QString("%0%").arg(QString::number(m_value)));
}
void SProgressBar::timerEvent(QTimerEvent *event)
{
Q_UNUSED(event);
this->update();
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
#include
#include
#include "sprogressbar.h"
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
void initUI();
private slots:
void onValueChanged(int value);
void onPercentStyle(int type);
void onWaterDesity(int value);
void onColorClicked();
void onWaterHeight(int value);
void onBorderWidth(int value);
void onTextColorClicked();
void onBoderColorClicked();
void onBgColorClicked();
private:
SProgressBar *m_progressBar = Q_NULLPTR;
QLabel* m_valueLab = Q_NULLPTR;
QSlider* m_valueSlider = Q_NULLPTR;
QLabel* m_percentStyleLab = Q_NULLPTR;
QComboBox* m_percentStyleComboBox = Q_NULLPTR;
QLabel* m_waterDesityLab = Q_NULLPTR;
QSlider* m_waterDesitySlider = Q_NULLPTR;
QLabel* m_colorLab = Q_NULLPTR;
QPushButton* m_colorBtn = Q_NULLPTR;
QLabel* m_waterHeightLab = Q_NULLPTR;
QSlider* m_waterHeightSlider = Q_NULLPTR;
QLabel* m_borderWidthLab = Q_NULLPTR;
QSlider* m_borderWidthSlider = Q_NULLPTR;
QLabel* m_textColorLab = Q_NULLPTR;
QPushButton* m_textColorBtn = Q_NULLPTR;
QLabel* m_boderColorLab = Q_NULLPTR;
QPushButton* m_boderColorBtn = Q_NULLPTR;
QLabel* m_bgColorLab = Q_NULLPTR;
QPushButton* m_bgColorBtn = Q_NULLPTR;
};
#endif // WIDGET_H
#include "widget.h"
#include
#include
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->setWindowIcon(QIcon(":/Demo_Hello.ico"));
this->setWindowTitle("水波纹Demo");
initUI();
}
void Widget::initUI()
{
this->setFixedSize(900,500);
m_progressBar = new SProgressBar(this);
m_progressBar->resize(600,500);
m_progressBar->move(0,0);
m_valueLab = new QLabel(this);
m_valueLab->setAlignment(Qt::AlignCenter);
m_valueLab->resize(100,30);
m_valueLab->move(600,0);
m_valueLab->setText("\350\277\233\345\272\246"); // 进度
m_valueSlider = new QSlider(this);
m_valueSlider->setOrientation(Qt::Horizontal);
m_valueSlider->resize(180,20);
m_valueSlider->move(700,5);
m_valueSlider->setMaximum(m_progressBar->maxValue());
m_valueSlider->setMinimum(m_progressBar->minValue());
m_valueSlider->setValue(m_progressBar->value());
connect(m_valueSlider, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int)));
m_percentStyleLab = new QLabel(this);
m_percentStyleLab->setAlignment(Qt::AlignCenter);
m_percentStyleLab->resize(100,30);
m_percentStyleLab->move(600,40);
m_percentStyleLab->setText("\345\275\242\347\212\266"); // 形状
m_percentStyleComboBox = new QComboBox(this);
m_percentStyleComboBox->resize(180,30);
m_percentStyleComboBox->move(700,40);
m_percentStyleComboBox->addItem("\347\237\251\345\275\242"); // 矩形
m_percentStyleComboBox->addItem("\345\234\206\345\275\242"); // 圆形
m_percentStyleComboBox->addItem("\346\244\255\345\234\206"); // 椭圆
m_percentStyleComboBox->setCurrentIndex(m_progressBar->percentStyle());
connect(m_percentStyleComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onPercentStyle(int)));
m_waterDesityLab = new QLabel(this);
m_waterDesityLab->setAlignment(Qt::AlignCenter);
m_waterDesityLab->resize(100,30);
m_waterDesityLab->move(600,80);
m_waterDesityLab->setText("\346\260\264\346\263\242\347\232\204\345\257\206\345\272\246"); // 水波的密度
m_waterDesitySlider = new QSlider(this);
m_waterDesitySlider->setOrientation(Qt::Horizontal);
m_waterDesitySlider->resize(180,20);
m_waterDesitySlider->move(700,85);
m_waterDesitySlider->setMaximum(100);
m_waterDesitySlider->setMinimum(0);
m_waterDesitySlider->setValue(m_progressBar->waterDensity());
connect(m_waterDesitySlider, SIGNAL(valueChanged(int)), this, SLOT(onWaterDesity(int)));
m_colorLab = new QLabel(this);
m_colorLab->setAlignment(Qt::AlignCenter);
m_colorLab->resize(100,30);
m_colorLab->move(600,120);
m_colorLab->setText("\350\203\214\346\231\257\351\242\234\350\211\262"); // 背景颜色
m_colorBtn = new QPushButton(this);
m_colorBtn->resize(180,20);
m_colorBtn->move(700,125);
QString styleCol = QString("background-color:rgb(%0,%1,%2)").arg(m_progressBar->color().red())
.arg(m_progressBar->color().green()).arg(m_progressBar->color().blue());
m_colorBtn->setStyleSheet(styleCol);
connect(m_colorBtn, SIGNAL(clicked(bool)),this, SLOT(onColorClicked()));
m_waterHeightLab = new QLabel(this);
m_waterHeightLab->setAlignment(Qt::AlignCenter);
m_waterHeightLab->resize(100,30);
m_waterHeightLab->move(600,160);
m_waterHeightLab->setText("\346\260\264\347\232\204\351\253\230\345\272\246"); // 水的高度
m_waterHeightSlider = new QSlider(this);
m_waterHeightSlider->setOrientation(Qt::Horizontal);
m_waterHeightSlider->resize(180,20);
m_waterHeightSlider->move(700,165);
m_waterHeightSlider->setMaximum(100);
m_waterHeightSlider->setMinimum(0);
m_waterHeightSlider->setValue(m_progressBar->waterHeight()*1000);
connect(m_waterHeightSlider, SIGNAL(valueChanged(int)), this, SLOT(onWaterDesity(int)));
m_borderWidthLab = new QLabel(this);
m_borderWidthLab->setAlignment(Qt::AlignCenter);
m_borderWidthLab->resize(100,30);
m_borderWidthLab->move(600,200);
m_borderWidthLab->setText("\350\276\271\346\241\206\345\256\275\345\272\246"); // 边框宽度
m_borderWidthSlider = new QSlider(this);
m_borderWidthSlider->setOrientation(Qt::Horizontal);
m_borderWidthSlider->resize(180,20);
m_borderWidthSlider->move(700,205);
m_borderWidthSlider->setMaximum(20);
m_borderWidthSlider->setMinimum(0);
m_borderWidthSlider->setValue(m_progressBar->borderWidth());
connect(m_borderWidthSlider, SIGNAL(valueChanged(int)), this, SLOT(onBorderWidth(int)));
m_textColorLab = new QLabel(this);
m_textColorLab->setAlignment(Qt::AlignCenter);
m_textColorLab->resize(100,30);
m_textColorLab->move(600,240);
m_textColorLab->setText("\345\255\227\344\275\223\351\242\234\350\211\262"); // 字体颜色
m_textColorBtn = new QPushButton(this);
m_textColorBtn->resize(180,20);
m_textColorBtn->move(700,245);
QString textCol = QString("background-color:rgb(%0,%1,%2)").arg(m_progressBar->textColor().red())
.arg(m_progressBar->textColor().green()).arg(m_progressBar->textColor().blue());
m_textColorBtn->setStyleSheet(textCol);
connect(m_textColorBtn, SIGNAL(clicked(bool)),this, SLOT(onTextColorClicked()));
m_boderColorLab = new QLabel(this);
m_boderColorLab->setAlignment(Qt::AlignCenter);
m_boderColorLab->resize(100,30);
m_boderColorLab->move(600,280);
m_boderColorLab->setText("\350\276\271\346\241\206\351\242\234\350\211\262"); // 边框颜色
m_boderColorBtn = new QPushButton(this);
m_boderColorBtn->resize(180,20);
m_boderColorBtn->move(700,285);
QString boderCol = QString("background-color:rgb(%0,%1,%2)").arg(m_progressBar->boderColor().red())
.arg(m_progressBar->boderColor().green()).arg(m_progressBar->boderColor().blue());
m_boderColorBtn->setStyleSheet(boderCol);
connect(m_boderColorBtn, SIGNAL(clicked(bool)),this, SLOT(onBoderColorClicked()));
m_bgColorLab = new QLabel(this);
m_bgColorLab->setAlignment(Qt::AlignCenter);
m_bgColorLab->resize(100,30);
m_bgColorLab->move(600,320);
m_bgColorLab->setText("\346\234\252\345\241\253\345\205\205\351\242\234\350\211\262"); // 未填充颜色
m_bgColorBtn = new QPushButton(this);
m_bgColorBtn->resize(180,20);
m_bgColorBtn->move(700,325);
QString bgCol = QString("background-color:rgb(%0,%1,%2)").arg(m_progressBar->bgColor().red())
.arg(m_progressBar->bgColor().green()).arg(m_progressBar->bgColor().blue());
m_bgColorBtn->setStyleSheet(bgCol);
connect(m_bgColorBtn, SIGNAL(clicked(bool)),this, SLOT(onBgColorClicked()));
}
void Widget::onValueChanged(int value)
{
m_progressBar->setValue(value);
}
void Widget::onPercentStyle(int type)
{
SProgressBar::PercentStyle_Type index = SProgressBar::PercentStyle_Type(type);
m_progressBar->setPercentStyle(index);
}
void Widget::onWaterDesity(int value)
{
m_progressBar->setWaterDensity(value);
}
void Widget::onColorClicked()
{
QColor c = QColorDialog::getColor(m_progressBar->color());
if(c.isValid()) {
m_progressBar->setColor(c);
QString styleCol = QString("background-color:rgb(%0,%1,%2)").arg(c.red()).arg(c.green()).arg(c.blue());
m_colorBtn->setStyleSheet(styleCol);
}
}
void Widget::onWaterHeight(int value)
{
m_progressBar->setWaterHeight(double(value/1000.0));
}
void Widget::onBorderWidth(int value)
{
m_progressBar->setBorderWidth(value);
}
void Widget::onTextColorClicked()
{
QColor c = QColorDialog::getColor(m_progressBar->textColor());
if(c.isValid()) {
m_progressBar->setTextColor(c);
QString styleCol = QString("background-color:rgb(%0,%1,%2)").arg(c.red()).arg(c.green()).arg(c.blue());
m_textColorBtn->setStyleSheet(styleCol);
}
}
void Widget::onBoderColorClicked()
{
QColor c = QColorDialog::getColor(m_progressBar->boderColor());
if(c.isValid()) {
m_progressBar->setBoderColor(c);
QString styleCol = QString("background-color:rgb(%0,%1,%2)").arg(c.red()).arg(c.green()).arg(c.blue());
m_boderColorBtn->setStyleSheet(styleCol);
}
}
void Widget::onBgColorClicked()
{
QColor c = QColorDialog::getColor(m_progressBar->bgColor());
if(c.isValid()) {
m_progressBar->setBgColor(c);
QString styleCol = QString("background-color:rgb(%0,%1,%2)").arg(c.red()).arg(c.green()).arg(c.blue());
m_bgColorBtn->setStyleSheet(styleCol);
}
}