首先上實例
要模擬波浪,就要首先畫出一條波浪線,正弦余弦曲線就很適合。
y=A*sin(ω*x+φ)+k
y=A*cos(ω*x+φ)+k
這是正弦余弦曲線的公式,要想實現水波效果,那需要兩條曲線,一條曲線的波峰對着另外一條曲線的波谷,要實現這樣的曲線效果,只有讓正弦曲線前移π/2個單位。所以我們最后對兩個水波使用的公式就變成了下面兩個
y=A*sin(ω*x-π/2+φ)+k
y=A*cos(ω*x+φ)+k
在我們的代碼中A就是水波的高度,ω是水波的周期,φ是水波的偏移量,用於實現動畫效果,k是水波的高度,注意由於我們的電腦坐標系是以左上角為原點,這和我們高中坐標系理解的不同,所以這里水波的高度是要進行一段處理的。
下面直接上代碼
#include <QProgressBar> #include <QTimer> #include <QPainter> #include <QColor> #include <QtMath> class WaterProgressBar : public QProgressBar { Q_OBJECT public: WaterProgressBar(QWidget *parent); ~WaterProgressBar(); protected: //頁面重繪事件 void paintEvent(QPaintEvent *event); private: void drawBackGround(QPainter* painter); void drawWaterWave(QPainter* painter); void drawText(QPainter* painter); private: int m_iBorderWidth;//邊框厚度 int m_iValue;//當前進度條進度 double m_dOffset;//水波偏移量 QColor m_waterColor;//水波顏色 QColor m_backgroundColor;//背景顏色 QColor m_borderColor;//邊框顏色 QColor m_textColor;//文本顏色 QTimer *m_timer;//控制水波移動的定時器 };
#include "stdafx.h" #include "WaterProgressBar.h" WaterProgressBar::WaterProgressBar(QWidget *parent) { m_iBorderWidth = 0; m_waterColor.setRgb(43, 123, 234); m_backgroundColor.setRgb(255, 255, 255); m_borderColor.setRgb(120, 120, 120); m_textColor.setRgb(0, 0, 0); m_dOffset = 0; //利用定時器固定時間內刷新頁面,使得水浪動起來 m_timer = new QTimer(this); m_timer->setSingleShot(false); connect(m_timer, &QTimer::timeout, this, [=](){ if (this->isVisible()) { //偏移量控制,每次繪制自加0.3,當超過一個正弦余弦2π周期時,就回退為0,加上定時器每50ms繪制一下,就觸發了曲線的動態效果 m_dOffset += 0.3; if (m_dOffset > 2 * M_PI) { m_dOffset = 0; } this->update(); } }); m_timer->start(50); } WaterProgressBar::~WaterProgressBar() { } void WaterProgressBar::paintEvent(QPaintEvent *event) { //進度條不可見的情況下就不重繪了 if (!this->isVisible()) { return; } m_iValue = this->value() < 0 ? 0 : this->value();//初始化時QT進度條的進度為-1,避免負進度的出現 QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);//開啟圖和字體抗鋸齒 drawBackGround(&painter);//繪制背景 drawWaterWave(&painter);//繪制水波 drawText(&painter);//繪制進度文本 } void WaterProgressBar::drawBackGround(QPainter* painter) { int width = this->width(); int height = this->height(); if (m_iBorderWidth > 0) { //根據窗口的長寬最小值得到外部背景直徑 int max_diameter = qMin(width, height); painter->save(); painter->setBrush(QBrush(m_borderColor)); painter->setPen(Qt::NoPen); //繪制邊框背景,然后用內部背景覆蓋,即可得到邊框 painter->drawEllipse(0, 0, max_diameter, max_diameter); painter->restore(); } painter->save(); //根據窗口的長寬最小值減去邊框厚度得到內部背景直徑 int min_diameter = qMin(width, height) - (2 * m_iBorderWidth); painter->setBrush(QBrush(m_backgroundColor)); painter->setPen(Qt::NoPen); painter->drawEllipse(m_iBorderWidth, m_iBorderWidth, min_diameter, min_diameter); painter->restore(); } void WaterProgressBar::drawWaterWave(QPainter* painter) { int width = this->width(); int height = this->height(); //根據窗口的長寬最小值減去邊框厚度得到直徑 int diameter = qMin(width, height) - (2 * m_iBorderWidth); //sincos曲線的波峰,波峰越大,水浪越高 double waveHeight = 0.04*diameter; //sincos曲線的周期,周期越大,水浪越密 double cycle = 2 * M_PI / diameter; //水的高度,可以理解為進度,注意由於我們的電腦坐標系是以左上角為原點,這和我們高中坐標系理解的不同 double percent = (double)m_iValue / 100; double waterHeight = (1 - percent)*diameter + m_iBorderWidth; painter->save(); QPainterPath totalPath; //加入圓形路徑 totalPath.addEllipse(m_iBorderWidth, m_iBorderWidth, diameter, diameter); //水波路徑,先畫淺色,再畫深色 QPainterPath water1; QPainterPath water2; water1.moveTo(m_iBorderWidth, m_iBorderWidth + diameter); water2.moveTo(m_iBorderWidth, m_iBorderWidth + diameter); //從左邊起始點到右邊結束點繪制兩條波浪曲線 for (int i = m_iBorderWidth; i <= m_iBorderWidth + diameter; i++) { double waterY1 = 0; double waterY2 = 0; if (m_iValue == 0 || m_iValue == 100) { waterY1 = waterY2 = waterHeight; } else { //第一條波浪Y軸 waterY1 = (double)(waveHeight * qSin(cycle * (i - m_iBorderWidth) - M_PI / 2 + m_dOffset)) + waterHeight;//當正弦曲線前進π/2,sin的波峰和cos的波谷就對上了 //第二條波浪Y軸 waterY2 = (double)(waveHeight * qCos(cycle * (i - m_iBorderWidth) + m_dOffset)) + waterHeight; } water1.lineTo(i, waterY1); water2.lineTo(i, waterY2); } //封閉 water1.lineTo(m_iBorderWidth + diameter, m_iBorderWidth + diameter); water2.lineTo(m_iBorderWidth + diameter, m_iBorderWidth + diameter); QPainterPath path; QColor waterColor1 = m_waterColor; waterColor1.setAlpha(100); QColor waterColor2 = m_waterColor; waterColor2.setAlpha(200); //第一條波浪 path = totalPath.intersected(water1); painter->setBrush(waterColor1); painter->setPen(Qt::NoPen); painter->drawPath(path); painter->restore(); painter->save(); //第二條波浪挖去后的路徑 path = totalPath.intersected(water2); painter->setBrush(waterColor2); painter->setPen(Qt::NoPen); painter->drawPath(path); painter->restore(); } void WaterProgressBar::drawText(QPainter* painter) { painter->save(); int width = this->width(); int height = this->height(); //根據窗口的長寬最小值減去邊框厚度得到直徑 int diameter = qMin(width, height) - (2 * m_iBorderWidth); int fontSize = diameter / 5; //設置文本字體 QFont font; font.setFamily("微軟雅黑"); font.setPixelSize(fontSize); font.setBold(true); //繪制文本 painter->setFont(font); painter->setPen(m_textColor); painter->drawText(QRectF(m_iBorderWidth, m_iBorderWidth, diameter, diameter), Qt::AlignCenter, QString("%1%").arg(m_iValue)); painter->restore(); }