Разработка
Паря в космосе: анимации с помощью Compose и Canvas
Как мы видим, даже небольшие изменения придают холсту большую подвижность.
В своей предыдущей статье «Рисуя звезды: рисование с помощью Compose и Canvas» я рассказала о том, как я хотела улучшить свои навыки работы с Canvas и Compose и создала иллюстрацию с планетами и звездами. В этой статье я расскажу, как анимировать эти элементы. В итоге результат будет выглядеть так:
Весь код доступен здесь.
Анимация
Звезды
Давайте начнем со звезд. В предыдущей статье мы определили класс данных Star следующим образом:
data class Star( val size: Float, val topLeft: Offset, val color: Color, )
Исходя из этих значений, мы хотим изменить размер звезд. Для этого мы определим множитель, который будем использовать вместе с размерами звезд для создания эффекта мерцания. Мы сделаем это с помощью infiniteTransition
и animateFloat
:
val infiniteTransition = rememberInfiniteTransition(label = "infinite") val starSizeMultiplierOne by infiniteTransition .animateFloat( initialValue = 1f, targetValue = 1.5f, animationSpec = infiniteRepeatable( animation = tween( durationMillis = 3000, easing = animationEasing, ), repeatMode = RepeatMode.Reverse, ), label = "starSizeMultiplierOne", )
Для starSizeMultiplierOne
мы зададим начальное значение 1, что означает, что, поскольку он будет действовать как множитель, размер будет равен 1 * size
. Целевое значение — 1.5, а поскольку режим повтора — Reverse
, звезды будут колебаться в размере между 1 и 1.5. Этот переход создает эффект увеличения и уменьшения.
Поскольку мы хотим, чтобы звезды выглядели реалистично и не анимировались с одинаковой скоростью, нам нужно определить два анимированных множителя. Давайте определим еще один:
val starSizeMultiplierTwo by infiniteTransition .animateFloat( initialValue = 0.7f, targetValue = 1.7f, animationSpec = infiniteRepeatable( animation = tween( durationMillis = 2300, easing = animationEasing, ), repeatMode = RepeatMode.Reverse, ), label = "starSizeMultiplierTwo", )
Этот мультипликатор имеет немного меньшую продолжительность анимации, а начальные и целевые значения отличаются, поэтому мерцание происходит с другой скоростью, чем в первом мультипликаторе.
Теперь нам нужно передать эти множители звездам:
val stars = starsList.mapIndexed { index, star -> val multiplier = if (index % 2 == 0) starSizeMultiplierOne else starSizeMultiplierTwo star.copy( size = star.size * multiplier, ) }
Мы просматриваем список звезд, определенный ранее, возвращаем копии звезд с прикрепленным к ним множителями и сохраняем их в новом списке. Позже, при рисовании звезд, мы используем этот новый список.
В случае этого фрагмента кода такой перебор списка может показаться излишним — почему бы просто не определить множители при определении звезд? В данном случае это сработает. Но если бы мы определяли звезды вне компонента, анимированные значения было бы сложно подключить, поэтому стратегия будет именно такой.
Планета
Следующая вещь, которую мы хотим анимировать, — это планета, точнее, луна, вращающаяся вокруг нее.
В предыдущем посте мы определили, как рисовать планету и луну, а в исходном коде была показана функция расширения под названием drawMoon
. Давайте расширим ее и передадим параметр под названием degrees
:
fun DrawScope.drawMoon( center: Offset, outlineStyle: Stroke, degrees: Float, ) { ... }
Затем мы обернем содержимое drawMoon
функцией rotate
, которая использует градусы для вращения и центр для поворота, чтобы вращение происходило вокруг центра планеты:
rotate(degrees = degrees, pivot = center) { ... }
В компоненте верхнего уровня мы определяем degrees
, которые представляют собой значения смещения анимации:
val degrees by infiniteTransition .animateFloat( initialValue = 360f, targetValue = 0f, animationSpec = infiniteRepeatable( animation = tween( durationMillis = 3000 * 6, easing = LinearEasing, ), ), label = "degrees", )
В качестве начального значения мы используем 360f, а в качестве целевого — 0f. Эти значения создают эффект, что луна вращается вокруг планеты против часовой стрелки.
Сатурн
Последний элемент, который мы анимируем, — это Сатурн. Его движение очень тонкое — он слегка перемещается вверх и вниз, создавая эффект парения.
Ранее мы определили функцию расширения drawSaturn
, которая принимает координаты левого верхнего угла и стиль контура. Мы можем использовать левые верхние координаты для создания эффекта.
Сначала давайте определим смещение центра — значение анимации, которое мы будем использовать:
val centerOffset by infiniteTransition .animateFloat( initialValue = 0f, targetValue = 2f, animationSpec = infiniteRepeatable( animation = tween( durationMillis = 3000, easing = EaseIn, ), repeatMode = RepeatMode.Reverse, ), label = "centerOffset", )
Как видите, начальное значение равно 0f, а целевое — 2f. Мы можем использовать это и изменить параметры, передаваемые в drawSaturn
, добавив centerOffset
к координатам левого верхнего угла:
drawSaturn( center = Offset( size.width * 0.25f + centerOffset, size.height * 0.25f + centerOffset ), outlineStyle = outlineStyle, )
Таким образом, координаты x и y получают дополнительную анимацию от 0f до 2f, создавая эффект парения.
Подведение итогов
В этой статье мы рассмотрели анимацию рисунков на Canvas. Во всех анимациях по-разному использовались эффекты анимации. И, как мы видим, даже небольшие изменения придают холсту большую подвижность.
Надеюсь, вам понравилась эта статья и вы чему-то научились!