Site icon AppTractor

Паря в космосе: анимации с помощью 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. Во всех анимациях по-разному использовались эффекты анимации. И, как мы видим, даже небольшие изменения придают холсту большую подвижность.

Надеюсь, вам понравилась эта статья и вы чему-то научились!

Источник

Exit mobile version