Как создать эффекты ввода и вывода слайдов с помощью QPropertyAnimation?

#c #qt

#c #qt

Вопрос:

Моя цель

Я хочу анимировать изображения в слайд-шоу следующим образом:

 << Animation Direction <<  
/* THIS IS EXPECTATION */  

                    --------------------
                    --------------------
    CURR IMAGE      ------ NEXT IMAGE --
                    --------------------
                    --------------------
<--- VIEW --------->

            --------------------
            --------------------
 IMAGE      ------ NEXT IMAGE --
            --------------------
            --------------------
<--- VIEW --------->

--------------------
--------------------
------ NEXT IMAGE --
--------------------
--------------------
<--- VIEW --------->
  

Что я пробовал

Я использую два экземпляра QPropertyAnimation :

 d->currImage      = new QLabel(ui->frmSlideshow);
d->currImage->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
d->animCurrImage  = new QPropertyAnimation(d->currImage, "geometry");

d->nextImage      = new QLabel(ui->frmSlideshow);
d->nextImage->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
d->animNextImage  = new QPropertyAnimation(d->nextImage, "geometry");

connect(d->animNextImage, SIGNAL(finished()),this, SLOT(applyBackground()));
connect(amp;(d->timer), SIGNAL(timeout()), this, SLOT(timer_timeout()));
d->timer.start(1500);
  

По истечении времени ожидания я делаю следующее:

 QPixmap pixCurrImage;
pixCurrImage.load(d->currImagePath());
d->currImage->setPixmap(pixCurrImage);

QPixmap pixNextImage;
pixNextImage.load(d->nextImagePath());
d->nextImage->setPixmap(pixNextImage);

d->currImage->show();
d->currImage->raise();
d->currImage->setFixedSize(ui->frmSlideshow->size());

d->nextImage->show();
d->nextImage->raise();
d->nextImage->setFixedSize(ui->frmSlideshow->size());

d->animCurrImage->setDuration(500);
d->animCurrImage->setStartValue(QRect(0, 0, ui->frmSlideshow->width(), ui->frmSlideshow->height()));
d->animCurrImage->setEndValue(QRect(-1*ui->frmSlideshow->width(), 0, ui->frmSlideshow->width(), ui->frmSlideshow->height()));

d->animNextImage->setDuration(500);
d->animNextImage->setStartValue(QRect(2*ui->frmSlideshow->width(), 0, ui->frmSlideshow->width(), ui->frmSlideshow->height()));
d->animNextImage->setEndValue(QRect(0, 0, ui->frmSlideshow->width(), ui->frmSlideshow->height()));

QParallelAnimationGroup * group = new QParallelAnimationGroup(this);
group->addAnimation(d->animCurrImage);
group->addAnimation(d->animNextImage);
group->start();
  

Моя проблема

Между обоими скользящими изображениями отображается небольшой разрыв (около 100 пикселей):

 << Animation Direction <<  
/* THIS IS HAPPENING*/  

               --------------------
               --------------------
 IMAGE         ------ NEXT IMAGE --
               --------------------
               --------------------
<--- VIEW --------->

      --------------------
      --------------------
      ------ NEXT IMAGE --
      --------------------
      --------------------
<--- VIEW --------->

--------------------
--------------------
------ NEXT IMAGE --
--------------------
--------------------
<--- VIEW --------->
  

Как заставить его либо работать должным образом, либо достичь желаемой анимации каким-либо другим способом?

Комментарии:

1. Взгляните на SlideView класс моей библиотеки FlatGUI и либо используйте его как есть, либо адаптируйте к своим потребностям.

Ответ №1:

Для достижения желаемой анимации я предлагаю вам взглянуть на HorizontalSlide класс FlatGUI библиотеки виджетов, написанный мной.

Там я использую QVariantAnimation для создания эффекта скольжения. Вот основные части HorizontalSlide , касающиеся анимации:

 void HorizontalSlide::start(bool slideLeft, int speed)
{
    QVariantAnimation *animation = new QVariantAnimation(this);

    m_slideLeft = slideLeft;

    m_labCurrent->show();
    m_labNext->show();

    animation->setStartValue(m_slideLeft ? 0 : m_width);
    animation->setEndValue(m_slideLeft ? m_width : 0);
    animation->setDuration(speed);
    animation->start(QVariantAnimation::DeleteWhenStopped);

    connect(animation, amp;QVariantAnimation::valueChanged,
            this, amp;HorizontalSlide::onValueChanged);
    connect(animation, amp;QVariantAnimation::finished,
            this, amp;HorizontalSlide::onAnimationFinished);
}

void HorizontalSlide::onValueChanged(const QVariant amp;value)
{
    int n = -value.toInt(), m = m_width   n;

    if (m_slideLeft) {
        m_labCurrent->move(n, 0);
        m_labNext->move(m, 0);
    } else {
        m_labCurrent->move(m, 0);
        m_labNext->move(n, 0);
    }

    m_parentWidget->repaint();
}

void HorizontalSlide::onAnimationFinished()
{
    delete m_labCurrent;
    delete m_labNext;

    m_nextPage->show();
    m_nextPage->resize(m_parentWidget->size());

    deleteLater();
    finished();
}
  

Я использую QVariantAnimation::valueChanged сигнал для QWidget::move моментального снимка виджетов, чтобы скользить по оси x -. Затем код реагирует на QAbstractAnimation::finished сигнал на QWidget::show и QWidget::resize на следующую страницу.

При условии, что у вас есть эти переменные, объявленные и инициализированные:

 QWidgetList pages;  
currentIndex(-1),
nextIndex(-1),
busy(false)
  

вы могли бы использовать HorizontalSlide в методе, подобном этому:

 void slideToPage(int n, int speed) {
    if (busy
        || pages.isEmpty()
        || (currentIndex < 0)
        || (n == currentIndex)
        || (speed < 0))
        return;

    auto *transition = new HorizontalSlide(this);

    busy = true;

    transition->setCurrentPage(pages.at(currentIndex));
    transition->setNextPage(pages.at(n));
    transition->start(n > currentIndex, speed);

    connect(transition, amp;HorizontalSlide::finished,
                     [this, transition](){
        currentIndex = pages.indexOf(transition->nextPage());
        busy = false;

        currentIndexChanged(currentIndex, pages.count());
        currentPageChanged(pages.at(currentIndex)->windowTitle());
    });
}
  

Вы также можете добавить библиотеку в свой проект и использовать ее SlideView класс.

Комментарии:

1. Спасибо, это было небольшое задание. Поэтому я не включил всю библиотеку

2. @Jai, моего комментария, как вы подтвердили, было достаточно, чтобы помочь вам. Однако для дальнейшего использования необходим более полный ответ, поэтому я также предоставил его.

3. Конечно, спасибо 🙂

Ответ №2:

ЗАГРУЗКА ИЗОБРАЖЕНИЙ

     d->currImage->setFixedSize(ui->frmSlideshow->size());
    d->currImage->move(0,0);
    QPixmap pixCurrImage;
    pixCurrImage.load(d->currImagePath());
    d->currImage->setPixmap(pixCurrImage);
    d->currImage->show();
    d->currImage->raise();

    d->nextImage->setFixedSize(ui->frmSlideshow->size());
    d->nextImage->move(ui->frmSlideshow->width(), 0); // So that  
    QPixmap pixNextImage;                             // you will not see
    pixNextImage.load(d->nextImagePath());            // glitch due 
    d->nextImage->setPixmap(pixNextImage);            // to change  
    d->nextImage->show();
    d->nextImage->raise();
  

Анимация (спасибо пользователю 6528273 за помощь)

     QVariantAnimation *animation = new QVariantAnimation(ui->frmSlideshow);
    animation->setStartValue(0);
    animation->setEndValue(ui->frmSlideshow->width());
    animation->setDuration(600);
    animation->start(QVariantAnimation::DeleteWhenStopped);

    connect(animation, SIGNAL(valueChanged(QVariant)),
            this, SLOT(onValueChanged(QVariant)));
  

СЛОТ — OnValueChanged()

 void onValueChanged(const QVariant amp;value)
{
    int n = -value.toInt();
    int m = ui->frmSlideshow->width()   n;
    d->currImage->move(n, 0);
    d->nextImage->move(m, 0);
    this->repaint();
}

/** ui->frmSlideshow is the frame, on which images are sliding **/