В этой статье мы создадим потрясающую 3D-анимацию Atomic Loader в Jetpack Compose.
Интересно? Давайте сделаем! ⚛️🚀
Вдохновение
Эта анимация загрузчика вдохновлена оригинальной работой, созданной Мартином ван Дриелем с помощью HTML и CSS, которая распространяется по MIT License.
Я воссоздал этот эффект с помощью Jetpack Compose, чтобы перенести его на Android.
Создание вращающегося круга
Для начала нам нужно создать composable функцию RotatingCircle
. Эта функция будет отображать один круг, который мы будем использовать для создания лоадера.
@Composable fun RotatingCircle( modifier: Modifier, rotationX: Float, rotationY: Float, rotationZ: Float, borderColor: Color, borderWidth: Dp ) { // Further implementation }
Рисуем круг
Чтобы добиться нужного эффекта, мы вычитаем один круг из другого и рисуем результат на Canvas
:
Canvas(modifier) { // Define the path for the main circle val mainCircle = Path().apply { addOval(Rect(size.center, size.minDimension / 2)) } // Adjust the position of the clipping circle to the left by borderWidth val clipCenter = Offset(size.width / 2f - borderWidth.toPx(), size.height / 2f) // Define the path for the clipping circle val clipCircle = Path().apply { addOval(Rect(clipCenter, size.minDimension / 2)) } // Subtract the clipping circle from the main circle val path = Path().apply { op(mainCircle, clipCircle, PathOperation.Difference) } // Draw the subtracted path drawPath(path, borderColor) }
Трансформация
Далее мы используем graphicsLayer
для поворота холста в трехмерном пространстве:
Canvas( modifier.graphicsLayer { this.rotationX = rotationX this.rotationY = rotationY this.rotationZ = rotationZ // To create a depth effect adjust the cameraDistance cameraDistance = 12f * density } ) { // Drawing logic }
Составляем лоадер
Для рендеринга загрузчика мы определяем композабл функцию AtomicLoader
:
@Composable fun AtomicLoader( modifier: Modifier, color: Color = Color.White, borderWidth: Dp = 3.dp, cycleDuration: Int = 1000 )
Параметры:
modifier
— модификатор, который будет применен к контейнеру загрузчикаcolor
— цвет загрузчикаborderWidth
— ширина границы загрузчикаcycleDuration
— продолжительность одного полного цикла вращения в миллисекундах
Создаем анимацию вращения
Чтобы анимировать загрузчик, мы зададим бесконечное вращение, которое будет циклически изменяться от 0 до 360 градусов:
val infiniteTransition = rememberInfiniteTransition("InfiniteAtomicLoaderTransition") val rotation by infiniteTransition.animateFloat( initialValue = 0f, targetValue = 360f, animationSpec = infiniteRepeatable( animation = tween(cycleDuration, easing = LinearEasing) ), label = "AtomicLoaderRotation" )
Размещаем круги
Наконец, мы разместим круги внутри блока Box
и применим анимацию вращения:
Box(modifier) { RotatingCircle( modifier = Modifier.matchParentSize(), rotationX = 35f, rotationY = -45f, rotationZ = -90f + rotation, borderColor = color, borderWidth = borderWidth ) RotatingCircle( modifier = Modifier.matchParentSize(), rotationX = 50f, rotationY = 10f, rotationZ = rotation, borderColor = color, borderWidth = borderWidth ) RotatingCircle( modifier = Modifier.matchParentSize(), rotationX = 35f, rotationY = 55f, rotationZ = 90f + rotation, borderColor = color, borderWidth = borderWidth ) }
Поздравляю! Мы сделали это. Полный код вы можете найти на GitHub. Давайте изучим возможности использования.
Практическое использование
Давайте создадим Box
с радиальным градиентным фоном и поместим загрузчик в центр, чтобы увидеть результат.
⚠️ Убедитесь, что загрузчик имеет четко определенный размер! В противном случае он может не отобразиться или вызвать неожиданное поведение.
Box( modifier = Modifier .fillMaxSize() .background( brush = Brush.radialGradient( listOf(Color(0xFF3C4B57), Color(0xFF1C262B)) ) ), contentAlignment = Alignment.Center ) { AtomicLoader( Modifier.size(100.dp) ) }
Вот что получилось: