Введение
Обычные анимации позиции изменяют координаты по прямой линии между начальным и конечным значением. PathAnimation позволяет анимировать движение элемента вдоль произвольной, заранее заданной кривой траектории — дугой, петлёй, сложной кривой — что необходимо для эффектов вроде летящих по дуге уведомлений, элементов, огибающих препятствия на экране, или декоративных круговых движений.
Концепция
Траектория задаётся через объект Path, состоящий из последовательности сегментов — PathLine (прямая линия к точке), PathQuad/PathCubic (квадратичная/кубическая кривая Безье с одной или двумя контрольными точками), PathArc (дуга окружности) — PathAnimation затем анимирует движение элемента вдоль этого пути за заданное время, опционально также поворачивая элемент в соответствии с направлением движения по кривой (orientation).
Пример кода: PathAnimation
import QtQuick
Item {
width: 400
height: 400
Rectangle {
id: movingItem
width: 30
height: 30
radius: 15
color: "#dc2626"
}
// Описание дугообразной траектории "взлёта" уведомления —
// от нижней части экрана к верхней с изгибом в сторону
Path {
id: flightPath
startX: 50; startY: 350
PathCubic {
x: 350; y: 50
control1X: 50; control1Y: 50 // первая контрольная точка тянет начало кривой вверх
control2X: 350; control2Y: 350 // вторая контрольная точка тянет конец кривой влево-вниз
}
}
PathAnimation {
id: flightAnimation
target: movingItem
path: flightPath
duration: 1200
easing.type: Easing.InOutQuad
orientationEntryDuration: 200 // плавный, а не мгновенный поворот в начале движения
}
MouseArea {
anchors.fill: parent
onClicked: flightAnimation.start()
}
}
// Круговая траектория через PathArc — типичный паттерн для орбитального движения
Path {
id: circularPath
startX: 200; startY: 100
PathArc {
x: 200; y: 300
radiusX: 100; radiusY: 100
direction: PathArc.Clockwise
}
PathArc {
x: 200; y: 100
radiusX: 100; radiusY: 100
direction: PathArc.Clockwise
}
}
PathAnimation {
target: orbitingItem
path: circularPath
duration: 3000
loops: Animation.Infinite // непрерывное круговое движение без остановки
}
Пояснения к коду
flightPath определяет S-образную кубическую кривую Безье от нижнего левого угла к верхнему правому — две контрольные точки (control1X/Y, control2X/Y) определяют форму изгиба кривой, не являясь сами по себе точками, через которые кривая обязательно проходит, а скорее «магнитами», притягивающими траекторию в свою сторону — подбор этих точек требует некоторого экспериментирования для достижения визуально желаемой формы кривой. PathAnimation с orientationEntryDuration показывает дополнительную возможность — постепенный, а не мгновенный поворот анимируемого элемента в направлении движения по кривой в момент начала анимации, что выглядит более плавно, чем резкий мгновенный разворот.
Круговая траектория через два последовательных PathArc (каждый — полукруг) формирует полную окружность, по которой элемент движется бесконечно (loops: Animation.Infinite) — этот паттерн типичен для декоративных, постоянно повторяющихся орбитальных анимаций (вращающийся индикатор загрузки, орбитальное движение элемента вокруг центра).
Подводные камни
- Подбор контрольных точек кривой Безье для достижения конкретной визуально желаемой формы траектории требует экспериментирования методом проб, не существует прямого, аналитического способа вычислить «правильные» значения контрольных точек для произвольной желаемой формы — для сложных, специфичных траекторий разработчики часто используют визуальные инструменты редактирования кривых (например, Qt Design Studio) вместо ручного подбора координат напрямую в коде.
PathAnimationопределяет только саму траекторию движения, но не синхронизирует с ней независимо анимируемые другие свойства (например, прозрачность, появляющуюся и исчезающую по ходу движения) — для комбинированных эффектов (движение по дуге + изменение прозрачности) нужна отдельная параллельная анимация прозрачности черезNumberAnimationвнутриParallelAnimation, синхронизированная по времени сPathAnimation, а не единый встроенный механизм.- Бесконечно повторяющаяся
PathAnimation(loops: Animation.Infinite), запущенная, но не остановленная при скрытии или удалении соответствующего экрана/компонента, продолжает выполняться в фоне, потребляя ресурсы на постоянный пересчёт позиции и перерисовку, даже когда анимируемый элемент не виден пользователю — стоит явно останавливать такие бесконечные анимации при скрытии содержащего их экрана (черезComponent.onDestructionили явную логику видимости), а не предполагать автоматическую остановку. - Производительность сложных, многосегментных путей с большим количеством
PathCubic/PathArcсегментов при частом запуске анимации (например, множественные одновременные летящие по сложной траектории элементы) может заметно нагружать вычисление позиции на каждом кадре — для большого числа одновременных анимаций по сложным путям стоит профилировать производительность отдельно от простых, линейных или эллиптических траекторий.