menu

Questions & Answers

How to create a 'zoom' animation like seen on Discord?

The animation I'm referring to:

https://i.imgur.com/fC2cTCY.gif (i reduced the speed of the animation, also, click on the image to enlarge it)

https://www.youtube.com/watch?v=bcPs_kILo10 (default speed)

What i already did:

// stackedwidget.h
class StackedWidget : public QStackedWidget
{

    Q_OBJECT
public:

    enum QEasingCurve::Type m_animationtype = QEasingCurve::Type::OutQuart;
    int m_speed = 1000;
    int m_now;
    int m_next;
    bool animation_active = false;
    QPoint m_pnow;
    QParallelAnimationGroup* animgroup;

    StackedWidget(QWidget* parent) : QStackedWidget(parent)
    {
        QWidget* widget = new QWidget();
        QGridLayout* widget_layout = new QGridLayout();
        widget->setLayout(widget_layout);

        QPushButton* button;
        for (size_t i = 0; i < 10; i++)
        {
            button = new QPushButton();
            button->setText("Button " + QString::number(i));
            button->setStyleSheet(R"(
                background-color:  #3c3f45;
                border-radius: 4px;            
                color: rgba(255, 255, 255, 0.4);
                font-size: 15px;
                font-weight: 600;   
            )");
            widget_layout->addWidget(button);
        }

        button->setText("Change Page");

        connect(button, &QPushButton::clicked, [=]
        {
            animimateToPage(1);
        });  



        QWidget* widget_2 = new QWidget();
        QGridLayout* widget_layout_2 = new QGridLayout();
        widget_2->setLayout(widget_layout_2);

        for (size_t i = 0; i < 10; i++)
        {
            button = new QPushButton();
            button->setText("================ " + QString::number(i));
            button->setStyleSheet(R"(
                background-color:  #3c3f45;
                border-radius: 4px;            
                color: rgba(255, 255, 255, 0.4);
                font-size: 15px;
                font-weight: 600;   
            )");
            widget_layout_2->addWidget(button);
        }

        button->setText("return");
        
        connect(button, &QPushButton::clicked, [this]
        {
            animimateToPage(0);
        });  



        addWidget(widget);
        addWidget(widget_2);
    }



    void animimateToPage(int index)
    {

        if (animation_active)
            return;

        animation_active = true;

        int now = currentIndex();
        int next = indexOf(widget(index));

        int offsetx = frameRect().width();
        int offsety = frameRect().height();

        widget(next)->setGeometry(0, 0, offsetx, offsety);

        QPoint pnext = widget(next)->pos();
        QPoint pnow = widget(now)->pos();
        m_pnow = pnow;
        //widget(next)->move(pnext.x() - offsetx, pnext.y() - offsety);

        QRect rnext = widget(next)->geometry();
        QRect rnow = widget(now)->geometry();

        int addw = 300;
        int addh = 100;

        widget(next)->show();
        widget(next)->raise();

        QPropertyAnimation* animnow = new QPropertyAnimation(widget(now), "geometry");
        animnow->setDuration(m_speed);
        animnow->setEasingCurve(m_animationtype);
        animnow->setStartValue(QRect(0, 0, rnow.width(), rnow.height()));
        animnow->setEndValue(QRect(0, 0, rnow.width() - addw, rnow.height() - addh));

        QGraphicsOpacityEffect* animnow_op_eff = new QGraphicsOpacityEffect();
        widget(now)->setGraphicsEffect(animnow_op_eff);
        QPropertyAnimation* animnow_op = new QPropertyAnimation(animnow_op_eff, "opacity");
        animnow_op->setDuration(m_speed);
        animnow_op->setStartValue(1);
        animnow_op->setEndValue(0);



        QPropertyAnimation* animnext = new QPropertyAnimation(widget(next), "geometry");
        animnext->setDuration(m_speed);
        animnext->setEasingCurve(m_animationtype);
        animnext->setStartValue(QRect(0, 0, rnext.width() - addw, rnext.height() - addh));
        animnext->setEndValue(QRect(0, 0, rnext.width(), rnext.height()));

        QGraphicsOpacityEffect* animnext_op_eff = new QGraphicsOpacityEffect();
        animnext_op_eff->setOpacity(0);
        widget(next)->setGraphicsEffect(animnext_op_eff);
        QPropertyAnimation* animnext_op = new QPropertyAnimation(animnext_op_eff, "opacity");
        animnext_op->setDuration(m_speed);
        animnext_op->setStartValue(0);
        animnext_op->setEndValue(1);



        animgroup = new QParallelAnimationGroup;
        animgroup->addAnimation(animnow);
        animgroup->addAnimation(animnext);
        animgroup->addAnimation(animnow_op);
        animgroup->addAnimation(animnext_op);



        QObject::connect(animgroup, &QParallelAnimationGroup::finished, this, &StackedWidget::animationDoneSlot);
        m_next = next;
        m_now = now;
                
        animgroup->start(QAbstractAnimation::DeleteWhenStopped);
    }



    void animationDoneSlot()
    {
        setCurrentIndex(m_next);
        widget(m_now)->hide();
        widget(m_now)->move(m_pnow);

        widget(m_now)->setGraphicsEffect(nullptr);
        widget(m_next)->setGraphicsEffect(nullptr);

        animation_active = false;
    }
};

#include "stackedwidget.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);

    ui.centralWidget->setStyleSheet("#centralWidget { background-color: #36393f; }");

    QGridLayout* layout = new QGridLayout();
    ui.centralWidget->setLayout(layout);

    StackedWidget* stacked_widget = new StackedWidget(this);    
    layout->addWidget(stacked_widget);

    return;
}

I have added some random buttons which colors similar to those seen in discord just to help check if the animation is 'correct'.

Result:

QtWidgetsApplication9_UPCjEsBztG.gif

Looks like there's some kind of flickering at the end of the animation.

My question is how to calculate the values to use in setStartValue setEndValue of the geometry animation.

Comments:
2023-01-20 00:00:21
Howabout using ScaleAnimator instead? If you apply this to StackView replaceEnter and replaceExit you can get a nice zooming effect, e.g. github.com/stephenquan/qmlonline6/wiki/Example-Zoom-Animatio‌​n
2023-01-20 00:00:21
@StephenQuan hi thanks Stephen, but how to do this in c++? i dont know QML
Answers(0) :