Введение
StackView — стандартный компонент Qt Quick Controls для организации навигации между экранами по принципу стека (новый экран «накладывается» сверху, возврат снимает верхний экран) — аналогично навигации в большинстве мобильных приложений. В статье разберём настройку анимированных переходов между экранами через StackView, дополняя более экзотический shader-based подход из статьи 271 стандартным, встроенным механизмом.
Концепция
StackView поддерживает настройку переходов через свойство pushEnter/pushExit/popEnter/popExit (анимации для появляющегося и исчезающего экрана при переходе «вперёд» через push() и «назад» через pop()) или через более комплексный replaceEnter/replaceExit для полной замены текущего экрана. Каждое из этих свойств принимает Transition, описывающий, что происходит с соответствующим экраном (новым или старым) во время перехода — например, сдвиг по горизонтали в сочетании с изменением прозрачности.
Пример кода: Анимированные переходы между
import QtQuick
import QtQuick.Controls
ApplicationWindow {
width: 400
height: 600
visible: true
StackView {
id: stackView
anchors.fill: parent
initialItem: homeScreen
// Переход при push() — новый экран появляется
pushEnter: Transition {
ParallelAnimation {
NumberAnimation { property: "x"; from: stackView.width; to: 0; duration: 300; easing.type: Easing.OutCubic }
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 200 }
}
}
// Переход при push() — старый экран уходит назад и затемняется
pushExit: Transition {
ParallelAnimation {
NumberAnimation { property: "x"; from: 0; to: -stackView.width * 0.3; duration: 300; easing.type: Easing.OutCubic }
NumberAnimation { property: "opacity"; from: 1; to: 0.5; duration: 300 }
}
}
// Переход при pop() — обратное движение к предыдущему экрану
popEnter: Transition {
ParallelAnimation {
NumberAnimation { property: "x"; from: -stackView.width * 0.3; to: 0; duration: 300; easing.type: Easing.OutCubic }
NumberAnimation { property: "opacity"; from: 0.5; to: 1; duration: 300 }
}
}
popExit: Transition {
ParallelAnimation {
NumberAnimation { property: "x"; from: 0; to: stackView.width; duration: 300; easing.type: Easing.OutCubic }
NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 200 }
}
}
}
Component {
id: homeScreen
Page {
title: "Главная"
Button {
anchors.centerIn: parent
text: "Открыть детали"
onClicked: stackView.push(detailsScreen)
}
}
}
Component {
id: detailsScreen
Page {
title: "Детали"
Button {
anchors.centerIn: parent
text: "Назад"
onClicked: stackView.pop()
}
}
}
}
Пояснения к коду
Четыре свойства перехода (pushEnter, pushExit, popEnter, popExit) полностью независимы и контролируют разные стороны одного и того же визуального перехода — pushEnter определяет, как ведёт себя новый экран при переходе вперёд (вход справа со сдвигом и нарастанием прозрачности), а pushExit — как ведёт себя старый, уходящий экран в тот же самый момент (лёгкий уход влево с частичным затемнением, создающий эффект «параллакса» глубины). ParallelAnimation объединяет одновременное изменение нескольких свойств (x и opacity) в рамках одного перехода, синхронизированных по продолжительности.
Симметричное определение popEnter/popExit как обратного движения (старый экран, который при push уходил влево, при pop возвращается обратно справа) создаёт ощущение логически согласованной, «обратимой» навигации — пользователь интуитивно воспринимает переход назад как зеркальное отражение перехода вперёд, что усиливает ощущение предсказуемости и качества навигации в приложении.
Подводные камни
stackView.widthвнутри определенияTransition, использованного как начальное/конечное значение анимации, может быть не определено корректно в момент инициализацииStackView, если переходы определены до того, как сам компонент получил итоговый размер — для надёжности стоит убедиться, что геометрияStackViewстабильна к моменту первого использования переходов, либо использовать привязки, которые пересчитываются динамически, а не захватывают значениеwidthединожды в момент создания.- Слишком долгая продолжительность переходов (
durationзначительно больше 300-400 мс) может создать ощущение медленного, «тормозящего» приложения, особенно если пользователь часто и быстро перемещается между экранами — переходы должны быть достаточно заметными для визуальной обратной связи, но не настолько долгими, чтобы заставлять пользователя ждать завершения анимации перед тем, как продолжить взаимодействие с приложением. - Несогласованность между анимацией перехода и реальным содержимым экрана, если содержимое страницы (например, список с собственной анимацией прокрутки) продолжает собственную независимую анимацию одновременно с анимацией перехода
StackView— это может создать визуально перегруженный, сбивающий с толку эффект; для чистоты восприятия перехода стоит по возможности приостанавливать или сбрасывать независимые анимации содержимого экрана на время самого перехода между экранами. - Повторное нажатие кнопки навигации во время еще не завершённого предыдущего перехода (например, быстрые повторные нажатия «Открыть детали») может поставить в очередь несколько одновременных push-операций —
StackViewобычно справляется с этим корректно на уровне базовой логики стека, но стоит явно протестировать пользовательский опыт при таком «нетерпеливом» взаимодействии, возможно, временно блокируя повторные нажатия кнопок навигации на время активного перехода.