天天看點

QT 帶有動畫的 圓形進度條 水波進度條

我們在使用其他軟體的時候,經常能看到各種絢麗的進度條,其中帶有水波波紋的進度條就是其中一個。對于PC端軟體開發使用的QT,隻是提供了process bar,樣式十分單一。 是以這次我們就使用QT的paint 以及定時器來實作類似的水波進度條。

先看一下最終效果圖:

QT 帶有動畫的 圓形進度條 水波進度條

這個是怎樣實作的呢? 我們仍然是在一個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();
}

           

我們來看看效果:

QT 帶有動畫的 圓形進度條 水波進度條

第二步,是需要繪制水波,這也是至關重要的一步。我們使用設定的顔色使用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();
}

           

這個時候我們看一下效果圖:

QT 帶有動畫的 圓形進度條 水波進度條

這時候就已經完成了大部分功能,不過很重要的是沒有顯示進度百分比。

第三步就是 完成文字百分比的顯示了,我們使用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

繼續閱讀