我們在使用其他軟體的時候,經常能看到各種絢麗的進度條,其中帶有水波波紋的進度條就是其中一個。對于PC端軟體開發使用的QT,隻是提供了process bar,樣式十分單一。 是以這次我們就使用QT的paint 以及定時器來實作類似的水波進度條。
先看一下最終效果圖:
這個是怎樣實作的呢? 我們仍然是在一個QWidget上的paintEvent上重繪實作圖形繪制 + QTimer 實作動畫效果。
首先,我們是在一個QWidget 上進行繪制,繪制是在paintEvent 裡面進行開始繪制。水波動畫是使用QTimer 定時器進行定時觸,重繪控件,造成水波波動的動畫。
我們照例來分解一下整個實作過程: 圓形背景(邊框+背景) + 水波進度 + 進度數值。
第一步,我們先繪制圓形的背景,這個背景包含邊框,以及未被占有的背景。這個背景是一個圓形,我們使用 drawEllipse函數:
void QPainter::drawEllipse(const QRectF &rectangle)
其中我們通過設定 畫筆QPen 的寬度來實作 邊框:
QPen pen;
pen.setColor(borderColor);
pen.setWidthF(borderWidth);
pen.setJoinStyle(Qt::RoundJoin);
painter->setPen(pen);
通過設定畫刷 QBrush 來設定未占用的的背景色:
painter->setBrush(QBrush(m_bgColor));
這樣就完成了第一步,背景的繪制:
void WaterProcess::drawBg(QPainter *painter)
{
int width = this->width();
int height = this->height();
int side = qMin(width, height) - m_borderWidth;
int startX = (width - side) * 0.5;
int startY = (height - side) * 0.5;
painter->save();
painter->setBrush(QBrush(m_bgColor));
if (m_borderWidth == 0) {
painter->setPen(Qt::NoPen);
} else {
QBrush brush(m_borderColor);
painter->setPen(QPen(brush, m_borderWidth, Qt::SolidLine));
}
painter->drawEllipse(startX, startY, side, side);
painter->restore();
}
我們來看看效果:
第二步,是需要繪制水波,這也是至關重要的一步。我們使用設定的顔色使用QPainterPath來設定,其中最重要的有兩個,第一個就是QPainterPath 的一個intersected(求相交、挖除)功能。
QPainterPath QPainterPath::intersected(const QPainterPath &p) const
這個功能是傳回兩個路徑相交(重合)的部分。我們來分析一下為什麼使用這個求相交的功能。
因為我們要實作水波,是以我們需要使用我們以前學過的正弦sin 或者餘弦cos 函數,進行模拟水波。
正弦曲線公式 y = A * qSin(ωx + φ) + k
- A : 表示振幅, 我們可以用作水波的高度。
- w : 表示周期,可以了解為水波密度,W越大,表示水波越密集。
- k : 辨別Y軸偏移量,可以了解為進度,取值是進度百分比
我們可以設定兩個透明度不同、起始位置有偏差的兩條正弦曲線互相交錯,形成波浪的起伏,更具有真實性。
我們的大路徑 totalPainterPath,是整個圓形。上面用到的正弦公式模拟的水波。我們就要使用到了上面提到的intersected相交函數,來去除額外不需要顯示的部分。
void WaterProcess::drawProcess(QPainter *painter)
{
int width = this->width();
int height = this->height();
int side = qMin(width, height) - (2 * m_borderWidth); //直徑
int startX = (width - side) * 0.5;
int startY = (height - side) *0.5;
int endX = startX + side;
int endY = startY + side;
double percent = (m_value * 1.0) / (m_maxValue - m_minValue);
double w = 2 * M_PI / endX;
double A = endY * m_waterHeight;
double k = endY * (1.0 - percent);
painter->save();
painter->setPen(Qt::NoPen);
painter->setBrush(m_usedColor);
QPainterPath totalPath;
//加入圓形路徑
totalPath.addEllipse(startX, startY, side, side);
//水波路徑
QPainterPath water1;
QPainterPath water2;
water1.moveTo(startX, endY);
water2.moveTo(startX, endY);
m_offset += 0.6;
if (m_offset > (endX / 2)) {
m_offset = 0;
}
for(int i = startX; i < endX; i++) {
//第一條波浪Y軸
double waterY1 = (double)(A * qSin(w * i + m_offset)) + k;
//第二條波浪Y軸
double waterY2;
waterY2 = (double)(A * qSin(w * i + m_offset + (endX / 2 * w))) + k;
water1.lineTo(i, waterY1);
water2.lineTo(i, waterY2);
if (m_value == m_minValue) {
waterY1 = endY;
}
if (m_value == m_maxValue) {
waterY1 = startY;
}
}
//封閉
water1.lineTo(endX, endY);
water2.lineTo(endX, endY);
QPainterPath path;
QColor waterColor1 = m_usedColor;
waterColor1.setAlpha(100);
QColor waterColor2 = m_usedColor;
waterColor2.setAlpha(200);
//第一條波浪
path = totalPath.intersected(water1);
painter->setBrush(waterColor1);
painter->drawPath(path);
//第二條波浪挖去後的路徑
path = totalPath.intersected(water2);
painter->setBrush(waterColor2);
painter->drawPath(path);
painter->restore();
}
這個時候我們看一下效果圖:
這時候就已經完成了大部分功能,不過很重要的是沒有顯示進度百分比。
第三步就是 完成文字百分比的顯示了,我們使用drawText函數進行函數繪制。
void QPainter::drawText(const QPointF &position, const QString &text)
這個就比較簡單了,隻需要算出來百分比就可以了,其實上一步我們已經完成了對百分比的算法了
void WaterProcess::drawValue(QPainter *painter)
{
painter->save();
int width = this->width();
int height = this->height();
int side = qMin(width, height) - m_borderWidth;
int startX = (width - side) * 0.5;
int startY = (height - side) * 0.5;
int fontSize = side / 3;
QFont font;
font.setFamily("微軟雅黑");
font.setPixelSize(fontSize);
font.setBold(true);
painter->setFont(font);
painter->setPen(Qt::white);
painter->drawText(QRectF(startX, startY, side, side), Qt::AlignCenter, QString("%1%").arg(m_value));
painter->restore();
}
到這裡我們就完成了所有動作的繪制,隻需要在paintEvent函數裡進行調用了
void WaterProcess::paintEvent(QPaintEvent *ev)
{
Q_UNUSED(ev)
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
//背景
drawBg(&painter);
//進度、水波
drawProcess(&painter);
//進度數字
drawValue(&painter);
}
在初始化的時候啟動定時器就好了!
寫在最後,例子已經上傳到csdn了,沒有積分的小夥伴,評論留下你的郵箱,看到後會立刻發出。
demo下載下傳位址
沒有積分支援的小夥伴,也可以通路、下載下傳git代碼,歡迎加星,會持續更新:https://github.com/xiezhongyuan/waterProcess