Qt实现卷轴式数字滚动效果

前言

有个需求要实现类似的功能,网上参考了别人代码自己修改了下。

方案一原文链接 Qt编写自定义控件:卷轴式数字滚动 ----作者: 友善啊,朋友

方案二原文链接 QT自定义控件–滑动选择器 ----作者: Qt程序员


提示:以下是本篇文章正文内容,下面案例可供参考

方案一:

效果展示

请添加图片描述
请添加图片描述
在这里插入图片描述


ReelWidget.h

代码如下(示例):

#ifndef REELWIDGET_H
#define REELWIDGET_H

#include <QWidget>
#include <QTimer>
#include <QPainter>
#include <QPaintEvent>
#include <QQueue>
#include <QDebug>

class ReelWidget : public QWidget
{
    Q_OBJECT

public:
    ReelWidget(QWidget *parent = nullptr);
    void initData();
    void setNewNumber(int newNumber);
    inline int getCurrentNumberValue(){return currentNumber;}
protected:
    void keyPressEvent(QKeyEvent *e)override;
    void paintEvent(QPaintEvent *)override;
    void wheelEvent(QWheelEvent *event) override;
    void mousePressEvent(QMouseEvent *event)override;
    void mouseReleaseEvent(QMouseEvent *event)override;
private slots:
    void onTimer();
private:
    void drawAUnit(const QRect &rect, QPainter * painter, int number);
public slots:
    void setValue(int flag);
private:
    enum class state
    {
        noRun,
        runing_up,
        runing_down
    };
    int number;
    int MAX_NUM;
    int MIN_NUM;
    int lastNumber;
    int currentNumber;
    int currentNumberA;
    int currentNumberB;
    int currentNumberC;
    const QPixmap bg;
    bool isRuning;
    bool isLeftPressDown;
    QPoint pressPoint;
    QTimer timer;
    QRect rectA;
    QRect rectB;
    QRect rectC;
    state widgetState{state::noRun};
    QRect whenRuningImageRect_Current;
    QRect whenRuningImageRect_Last;
};
#endif // REELWIDGET_H

ReelWidget.cpp

代码如下(示例):

#include "reelwidget.h"

int rateFactor = 5;

ReelWidget::ReelWidget(QWidget *parent)
    : QWidget(parent),bg{"://bg.png"}
{
    initData();
}

void ReelWidget::initData()
{
    number = 0;
    MAX_NUM = 10;
    MIN_NUM = -10;
    lastNumber = 0;
    currentNumber = 0;
    currentNumberA = 1;
    currentNumberB = 0;
    currentNumberC = -1;
    isRuning = false;
    isLeftPressDown = false;
    setFocusPolicy(Qt::StrongFocus);
    setFixedSize(200,90);
    auto font = this->font();
    font.setPixelSize(18);
    font.setFamily("方正小标宋简体");
    setFont(font);
    whenRuningImageRect_Last = QRect(this->rect().x(), -this->rect().height()/3, this->rect().width(), this->rect().height()/3);
    rectA = QRect(this->rect().x(), 0, this->rect().width(), this->rect().height()/3);
    rectB = QRect(this->rect().x(), this->rect().height()/3*1, this->rect().width(), this->rect().height()/3);
    rectC = QRect(this->rect().x(), this->rect().height()/3*2, this->rect().width(), this->rect().height()/3);
    connect(&timer,&QTimer::timeout,this,&ReelWidget::onTimer);
    timer.setInterval(60);
}

void ReelWidget::setNewNumber(int newNumber)
{
    if(this->widgetState == state::runing_up){
        lastNumber = currentNumberA;
        currentNumberA = newNumber;
        currentNumberB = currentNumberA-1;
        currentNumberC = currentNumberB-1;
        currentNumber = currentNumberB;
        whenRuningImageRect_Last = QRect(this->rect().x(), 0, this->rect().width(), this->rect().height()/3);
        rectA = QRect(this->rect().x(), this->rect().height()/3*1, this->rect().width(), this->rect().height()/3);
        rectB = QRect(this->rect().x(), this->rect().height()/3*2, this->rect().width(), this->rect().height()/3);
        rectC = QRect(this->rect().x(), this->rect().height(), this->rect().width(), this->rect().height()/3);
        timer.start();
    }
    else if(this->widgetState == state::runing_down){
        lastNumber = currentNumberC;
        currentNumberC = newNumber;
        currentNumberB = currentNumberC+1;
        currentNumberA = currentNumberB+1;
        currentNumber = currentNumberB;
        rectA = QRect(this->rect().x(), -this->rect().height()/3, this->rect().width(), this->rect().height()/3);
        rectB = QRect(this->rect().x(), 0, this->rect().width(), this->rect().height()/3);
        rectC = QRect(this->rect().x(), this->rect().height()/3*1, this->rect().width(), this->rect().height()/3);
        whenRuningImageRect_Last = QRect(this->rect().x(), this->rect().height()/3*2, this->rect().width(), this->rect().height()/3);
        timer.start();
    }
}

void ReelWidget::wheelEvent(QWheelEvent *event)
{
    if(event->delta() >= 120){
        if(currentNumber == MAX_NUM-1) return;
        widgetState = state::runing_down;
        setNewNumber(number);
        number++;
    }
    else{
        if(currentNumber == MIN_NUM+1) return;
        widgetState = state::runing_up;
        setNewNumber(number);
        number--;
    }
}

void ReelWidget::keyPressEvent(QKeyEvent *e)
{
    switch (e->key()) {
    case Qt::Key_Up:
        if(currentNumber == MAX_NUM-1) break;
        widgetState = state::runing_down;
        setNewNumber(number);
        number++;
        break;
    case Qt::Key_Down:
        if(currentNumber == MIN_NUM+1) break;
        widgetState = state::runing_up;
        setNewNumber(number);
        number--;
        break;
    default:
        break;
    }
}

void ReelWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setFont(this->font());
    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.setRenderHint(QPainter::SmoothPixmapTransform,true);
#if 0
    QRect rectBackGroundA = QRect(this->rect().x(), 0, this->rect().width(), this->rect().height()/3);
    QRect rectBackGroundB = QRect(this->rect().x(), this->rect().height()/3*1, this->rect().width(), this->rect().height()/3);
    QRect rectBackGroundC = QRect(this->rect().x(), this->rect().height()/3*2, this->rect().width(), this->rect().height()/3);
#if 0
    painter.drawPixmap(rectBackGroundA, bg.scaled(this->rect().size()));
    painter.drawPixmap(rectBackGroundC, bg.scaled(this->rect().size()));
    QPixmap pix("://bg1.png");
    painter.drawPixmap(rectBackGroundB, pix.scaled(this->rect().size()));
#endif
#if 0
    QColor colorBackGround = QColor(0, 0, 0);
    painter.setBrush(colorBackGround);
    painter.drawRect(rectBackGroundA);
    painter.drawRect(rectBackGroundC);
    colorBackGround = QColor(87, 87, 87);
    painter.setBrush(colorBackGround);
    painter.drawRect(rectBackGroundB);
#endif
#endif
    if(widgetState == state::runing_up)
    {
        drawAUnit(whenRuningImageRect_Last,&painter,lastNumber);
        drawAUnit(rectA,&painter,currentNumberA);
        drawAUnit(rectB,&painter,currentNumberB);
        drawAUnit(rectC,&painter,currentNumberC);
    }
    else if(widgetState == state::runing_down)
    {
        drawAUnit(rectA,&painter,currentNumberA);
        drawAUnit(rectB,&painter,currentNumberB);
        drawAUnit(rectC,&painter,currentNumberC);
        drawAUnit(whenRuningImageRect_Last,&painter,lastNumber);
    }
    else if(widgetState == state::noRun)
    {
        drawAUnit(rectA,&painter,currentNumberA);
        drawAUnit(rectB,&painter,currentNumberB);
        drawAUnit(rectC,&painter,currentNumberC);
    }
}

void ReelWidget::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        isLeftPressDown = true;
        pressPoint = event->globalPos();
    }
}

void ReelWidget::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton && isLeftPressDown){
        isLeftPressDown = false;
        QPoint movePos = event->globalPos();
        int moveY = movePos.y() - pressPoint.y();
        if(moveY >= 5){
            if(currentNumber == MAX_NUM-1) return;
            widgetState = state::runing_down;
            setNewNumber(number);
            number++;
        }
        else if(moveY <= -5){
            if(currentNumber == MIN_NUM+1) return;
            widgetState = state::runing_up;
            setNewNumber(number);
            number--;
        }
    }
}

void ReelWidget::onTimer()
{
    if(widgetState == state::runing_up){
        auto currentTop = rectA.top() - rateFactor;
        if(currentTop <= -5){
            timer.stop();
            widgetState = state::noRun;
            qDebug()<< "当前值: " << currentNumber;
        }
        else{
            whenRuningImageRect_Last.moveTop(whenRuningImageRect_Last.top() - rateFactor);
            rectA.moveTop(currentTop);
            rectB.moveTop(rectB.top() - rateFactor);
            rectC.moveTop(rectC.top() - rateFactor);
        }
    }
    else if(widgetState == state::runing_down){
        auto currentTop = rectA.top() + rateFactor;
        if(currentTop >= 5){
            timer.stop();
            widgetState = state::noRun;
            qDebug()<< "当前值: " << currentNumber;
        }
        else{
            rectA.moveTop(currentTop);
            rectB.moveTop(rectB.top() + rateFactor);
            rectC.moveTop(rectC.top() + rateFactor);
            whenRuningImageRect_Last.moveTop(whenRuningImageRect_Last.top() + rateFactor);
        }
    }
    update();
}

void ReelWidget::setValue(int flag)
{
    if(flag == 1){
        widgetState = state::runing_down;
        setNewNumber(number);
        number++;
    }
    else{
        widgetState = state::runing_up;
        setNewNumber(number);
        number--;
    }
}

void ReelWidget::drawAUnit(const QRect & rect, QPainter * painter, int number)
{
#if 1
    painter->drawPixmap(rect, bg.scaled(this->rect().size()));
    if(number == currentNumber){
        QPixmap pix("://bg1.png");
        painter->drawPixmap(rect, pix.scaled(this->rect().size()));
    }
#endif
#if 0
    //绘制背景色
    QColor colorBackGround = QColor(0, 0, 0);
    painter->setBrush(colorBackGround);
    if(number == currentNumber){
        colorBackGround = QColor(87, 87, 87);
    }
    painter->setBrush(colorBackGround);
    painter->drawRect(rect);
#endif
    painter->setPen(Qt::white);
    QString value = QString::number(number);
    if(number > 0) value = QString("+%1").arg(number);
    if(number == currentNumber){
        painter->setPen(QColor(51, 245, 4));
        auto font = this->font();
        font.setBold(true);
        painter->setFont(font);
    }
    painter->drawText(rect, Qt::AlignCenter, value);
}

方案二:

效果展示

请添加图片描述

请添加图片描述

ReelWidget.h

代码如下(示例):

#ifndef SLIDINGSELECTOR_H
#define SLIDINGSELECTOR_H

#include <QWidget>
#include <QDebug>
#include <QTimer>
#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>

class SlidingSelector : public QWidget
{
    Q_OBJECT
public:
    explicit SlidingSelector(QWidget *parent = nullptr);
    void initData();
    void initConnect();
    void paintEvent(QPaintEvent *);
    void wheelEvent(QWheelEvent *event) override;
    void mousePressEvent(QMouseEvent *event)override;
    void mouseMoveEvent(QMouseEvent *event)override;
    void mouseReleaseEvent(QMouseEvent *event)override;
    void paint_frame(QPainter *painter);
    void paint_num(QPainter *painter, int number, int mouse_move_length, int mid);
    void paint_parting_line(QPainter *painter);
    void keyPressEvent(QKeyEvent *e);
private slots:
    void onTimer();
private:
    int MAX_NUM;
    int MIN_NUM;
    int space;
    int num_size;
    int mouse_Press;
    int mouse_move_length;
    int Current_num;
    bool bUpMove;
    QTimer myTimer;
    QColor currentNum_color;
    QColor besideNum_color;
    QColor partingLine_color;
    QColor frame_color;
};
#endif // SLIDINGSELECTOR_H

ReelWidget.cpp

代码如下(示例):

#include "slidingselector.h"

SlidingSelector::SlidingSelector(QWidget *parent)
    : QWidget(parent)
{
    initData();
    initConnect();
}

void SlidingSelector::initData()
{
    MAX_NUM = 10;
    MIN_NUM = -10;
    space = 0;
    num_size = 9;
    mouse_Press;
    mouse_move_length = 0;
    Current_num = 0;
    bUpMove = false;
    currentNum_color = QColor(51, 245, 4);
    besideNum_color = QColor(255, 255, 255);
    partingLine_color = QColor(87, 87, 87);
    frame_color = QColor(50, 87, 50);
    myTimer.setInterval(60);
    setFixedSize(300, 240);
    setFocusPolicy(Qt::StrongFocus);
}

void SlidingSelector::initConnect()
{
    connect(&myTimer,&QTimer::timeout,this,&SlidingSelector::onTimer);
}

void SlidingSelector::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton){
        mouse_Press = event->globalPos().y();
    }
}

void SlidingSelector::mouseMoveEvent(QMouseEvent *event)
{
    mouse_move_length = event->globalPos().y() - mouse_Press;
    if(mouse_move_length <= -1 && Current_num == MIN_NUM+1){
        mouse_move_length = 0;
        return;
    }
    if(mouse_move_length >= 1 && Current_num == MAX_NUM-1){
        mouse_move_length = 0;
        return;
    }
    update();
}

void SlidingSelector::mouseReleaseEvent(QMouseEvent *event)
{
    if(mouse_move_length <= -1 && Current_num == MIN_NUM+1){
        return;
    }
    if(mouse_move_length >= 1 && Current_num == MAX_NUM-1){
        return;
    }
    int Height = height();
    bool result = false;
    if(mouse_move_length >= Height/8 && Current_num < MAX_NUM)
    {
        result = true;
        mouse_move_length = Height/4;
    }
    else{
        if(qAbs(mouse_move_length) >= Height/8 && Current_num > MIN_NUM)
        {
            result = true;
            mouse_move_length = -Height/4;
        }
    }
    if(result == false){
        mouse_Press = 0;
        mouse_move_length = 0;
    }
    update();
}

void SlidingSelector::keyPressEvent(QKeyEvent *e)
{
    switch (e->key()) {
    case Qt::Key_Up:
        if(Current_num == MIN_NUM+1) break;
        bUpMove = true;
        myTimer.start();
        break;
    case Qt::Key_Down:
        if(Current_num == MAX_NUM-1) break;
        bUpMove = false;
        myTimer.start();
        break;
    default:
        break;
    }
}

void SlidingSelector::onTimer()
{
    int Height = height();
    if(bUpMove){
        mouse_move_length -= Height/20;
        if(mouse_move_length <= -Height/4 && Current_num < MAX_NUM){
            myTimer.stop();
        }
    }
    else{
        mouse_move_length += Height/20;
        if(mouse_move_length >= Height/4 && Current_num > MIN_NUM){
            myTimer.stop();
        }
    }
    update();
}

void SlidingSelector::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    paint_frame(&painter);
#if 0
    paint_parting_line(&painter);
#endif
    int Height = height();
    if(mouse_move_length >= Height/4 && Current_num < MAX_NUM)
    {
        Current_num += 1;
        mouse_Press += Height/4;
        mouse_move_length -= Height/4;
    }
    if(mouse_move_length <= -Height/4 && Current_num > MIN_NUM)
    {
        Current_num -= 1;
        mouse_Press -= Height/4;
        mouse_move_length += Height/4;
    }
    //中间数字
    paint_num(&painter,Current_num,mouse_move_length,1);
    //上下两行数字
    if(Current_num != MAX_NUM)
        paint_num(&painter,Current_num+1,mouse_move_length-Height/4-20,0);
    if(Current_num != MIN_NUM)
        paint_num(&painter,Current_num-1,mouse_move_length+Height/4+20,0);
}

void SlidingSelector::wheelEvent(QWheelEvent *event)
{
    if(event->delta() >= 120){
        if(Current_num == MAX_NUM-1) return;
        bUpMove = false;
        myTimer.start();
    }
    else{
        if(Current_num == MIN_NUM+1) return;
        bUpMove = true;
        myTimer.start();
    }
}
void SlidingSelector::paint_frame(QPainter *painter)
{
    painter->save();
    painter->setPen(Qt::NoPen);
#if 0
    painter->setBrush(frame_color);
    painter->drawRect(space,space,width()-space*2,height()-space*2);
#endif
#if 1
    QRect rectAbove = QRect(space, height()/3*0, width(), height()/3);
    QRect rectCentre = QRect(space, height()/3*1, width(), height()/3);
    QRect rectBelow = QRect(space, height()/3*2, width(), height()/3);
    QColor colorBackGround = QColor(0, 0, 0);
    painter->setBrush(colorBackGround);
    painter->drawRect(rectAbove);
    painter->drawRect(rectBelow);
    colorBackGround = QColor(87, 87, 87);
    painter->setBrush(colorBackGround);
    painter->drawRect(rectCentre);
#endif
    painter->restore();
}
void SlidingSelector::paint_num(QPainter *painter,int number,int mouse_move_length,int mid)
{
    painter->save();
    int Width = this->width();
    int Height = this->height();
    int size = qAbs((Height - qAbs(mouse_move_length))/num_size);//像素尺寸
    int height = Height/2-3*qAbs(mouse_move_length);//数字框宽度
    int y = Height/2+mouse_move_length-height/2;//数字框x坐标
    QFont font;
    font.setPixelSize(size);
    painter->setFont(font);
    if(mid == 1)
        painter->setPen(currentNum_color);
    else
        painter->setPen(besideNum_color);
    QString value = QString::number(number);
    if(number > 0) value = QString("+%1").arg(number);
    painter->drawText(QRectF(0, y, Width, height),Qt::AlignCenter, value);
    painter->restore();
}
void SlidingSelector::paint_parting_line(QPainter *painter)
{
    painter->save();
    QPen pen;
    pen.setBrush(partingLine_color);
    pen.setWidth(4);
    pen.setCapStyle(Qt::RoundCap);
    pen.setStyle(Qt::SolidLine);
    painter->setPen(pen);
    int up_down_space = width()/10;
    QPoint line_left_up = QPoint(space+up_down_space,space+(height()-space*2)/3);
    QPoint line_left_down = QPoint(space+(width()-space*2)-up_down_space,space+(height()-space*2)/3);
    QPoint line_right_up = QPoint(space+up_down_space,space+((height()-space*2)/3)*2);
    QPoint line_right_down = QPoint(space+(width()-space*2)-up_down_space,space+((height()-space*2)/3)*2);
    painter->drawLine(line_left_up,line_left_down);//绘制左侧分割线
    painter->drawLine(line_right_up,line_right_down);//绘制右侧分割线
    painter->restore();
}

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值