Створення логотипа Perplexity з матового скла у Jetpack Compose

У цьому туторіалі ми створимо анімований логотип Perplexity, що має ефект матового скла. Ми використаємо бібліотеку haze від Chris Banes для імітації скла та graphicsLayer для 3D-анімації

Налаштування проєкту

Спочатку необхідно додати залежність для haze у build.gradle:

repositories {
    mavenCentral()
}

dependencies {
    implementation("dev.chrisbanes.haze:haze:<version>")
}

Основний компонент

PerplexityLogo містить два блоки (RoundedBoxLeft і RoundedBoxRight), які створюють ефект матового скла.

💡 Таке рішення було зроблено через те, що після обертання елемента на 180° зображення відзеркалювалося і не давало такий ефект, як я хотів. На зображенні знизу видно, що жовтий є як зліва, так і зправа
Зображення відзеркалювалося після 180°
@Composable
fun PerplexityLogo(
    hazeStateLeft: HazeState,
    hazeStateRight: HazeState,
    modifier: Modifier
) {
    val containerColor = MaterialTheme.colorScheme.surface
    val hazeStyle = HazeStyle(
        backgroundColor = containerColor,
        tints = listOf(
            HazeTint(containerColor.copy(alpha = if (containerColor.luminance() >= 0.5) 0.3f else 0.1f))
        ),
        blurRadius = 10.dp,
        noiseFactor = 0.3f
    )

    val infiniteTransition = rememberInfiniteTransition()
    val animationProgress by infiniteTransition.animateFloat(
        initialValue = 1f,
        targetValue = 0f,
        animationSpec = infiniteRepeatable(
            animation = tween(5_000),
            repeatMode = RepeatMode.Restart
        )
    )

    Box(modifier = modifier) {
        val pages = 8
        MutableList(pages) { index ->
            val rotationAngle = (animationProgress * 360 + (index * 360 / pages)) % 360
            if (rotationAngle in 0f..180f) {
                RoundedBoxLeft(rotationAngle - 90f, hazeStateLeft, hazeStyle, Modifier)
            } else {
                RoundedBoxRight(rotationAngle + 90f, hazeStateRight, hazeStyle, Modifier)
            }
        }
    }
}


1/ Я створив свій HazeStyle, бо стандартні не дуже підходили, тай хотілося погратися з різним blurRadius та noiseFactor

  val hazeStyle = HazeStyle(
        backgroundColor = containerColor,
        tints = listOf(
            HazeTint(containerColor.copy(alpha = if (containerColor.luminance() >= 0.5) 0.3f else 0.1f))
        ),
        blurRadius = 10.dp,
        noiseFactor = 0.3f
    )

2/ Створюємо 8 сторінок та вираховуємо для кожної з них кут. Залежно від кута вони будуть або злівої чи правої сторони. Ми віднімаємо 90 чи додаємо 90 градусів, щоб у нас сторінки малювалися з однієї точки, залежно від того, звідки ми починаємо малювати

RoundedBox

@Composable
fun RoundedBoxLeft(rotationAngle: Float, hazeState: HazeState, hazeStyle: HazeStyle, modifier: Modifier) {
    Page(
        hazeState = hazeState,
        hazeStyle = hazeStyle,
        borderColor = Color(0xFF24F4FE),
        modifier = modifier.graphicsLayer {
            rotationY = rotationAngle
            rotationX = -45f
            cameraDistance = 100f
            transformOrigin = TransformOrigin(1f, 0f)
        }
    )
}

Основною різницею між лівою та правою частиною є transformOrigin = TransformOrigin(1f, 0f) та TransformOrigin(1f, 0f)

Ці блоки використовують graphicsLayer для створення ефекту перспективи

Page

@Composable
fun Page(
    hazeState: HazeState,
    hazeStyle: HazeStyle,
    borderColor: Color,
    modifier: Modifier
) {
    Box(
        modifier = modifier
            .hazeEffect(hazeState, style = hazeStyle)
            .border(8.dp, borderColor)
    )
}

Тут все просто. Box з обводкою та нашим ефектом скла. При необхідності можна задати заливки чи паралакс ефект

zIndex

У нашому прикладі є дуже важливим zIndex. Для лівої сторони ми його визначаємо як zIndex(rotationAngle) для правої zIndex(360 - rotationAngle)

Якщо не задати zIndex

hazeSource

Для отримання ефекту розмиття спочатку потрібно отримати інформацію з картинки позаду. Тому ми hazeState витягнули з PerplexityLogo ззовні, де є наш бекграунд і за допомогою hazeSource отримуємо його. Обов’язково потрібно вказати zIndex = 0f.

Box(
                contentAlignment = Alignment.Center
            ) {
                val hazeStateLeft = remember { HazeState() }
                val hazeStateRight = remember { HazeState() }
                Box(
                    modifier = Modifier
                        .offset(y = (-30).dp)
                        .size(300.dp)
                        .hazeSource(hazeStateRight, zIndex = 0f)
                        .hazeSource(hazeStateLeft, zIndex = 0f)
                        .graphicsLayer {
                            rotationZ = animationProgress * 1080
                        }
                        .clip(CircleShape)
                        .paint(
                            painter = painterResource(id = R.drawable.orb_bg),
                            contentScale = ContentScale.Crop
                        )

                )
                PerplexityLogo(
                    hazeStateLeft,
                    hazeStateRight,
                    modifier = Modifier.size(200.dp)
                )
            }

А в PerplexityLogo ми вже вказуємо zIndex залежно від оберту: .hazeSource(hazeStateLeft, rotationAngle + 1f) — за тим самим принципом, що й zIndex, але з +1 (оскільки попередній шар був 0).

Результат

Цей код створює анімований логотип Perplexity з ефектом матового скла. Використовуючи HazeEffect та graphicsLayer, ми досягаємо красивого візуального ефекту з плавною анімацією.

Результат


🔗 Посилання на репозиторій з кодом: https://github.com/rmnkhr/perplexity_ui_experiment


🔗 Посилання на канал по Android в тг
https://t.me/android_fragment

Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Roman K
Roman K@rmnkhr

599Прочитань
2Автори
12Читачі
На Друкарні з 15 квітня

Більше від автора

  • Шпаргалка по Kotlin ч. 1 Колекції

    Колеції та їх функції, які мало хто використовує, але можуть бути дуже корисними в написанні коду.

    Теми цього довгочиту:

    Kotlin
  • Як перемагати на хакатонах

    Розкажу про хакатони, у яких я брав участь з 2015 року. Ідеї, презентації, перемоги, висновки, смішні ситуації та меми

    Теми цього довгочиту:

    Hackathon
  • Як працює ChatGPT простими словами

    Спробуємо розібратися як працють нейронки та в чому унікальність ChatGPT

    Теми цього довгочиту:

    Chat Gpt

Вам також сподобається

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Вам також сподобається