Kasus penggunaan blur umum

Bagian berikut menunjukkan cara menggabungkan Modifier.blur, BlurRadiusSpec, BlurStop, dan BlurScope untuk pola UI umum. Untuk pengantar API ini, lihat Membuat blur di Compose.

Memburamkan tepi konten yang dapat di-scroll atau ditarik

Saat menampilkan daftar scroll, carousel, atau pemilih, Anda dapat menggunakan blur progresif multi-stop untuk melembutkan item di dekat tepi penampung sambil menjaga area tengah yang aktif tetap fokus.

Memudarkan tepi atas dan bawah daftar scroll

Untuk memperhalus item saat di-scroll ke dalam dan ke luar area pandang daftar vertikal, terapkan BlurRadiusSpec.verticalGradient dengan entri BlurStop ke LazyColumn. Dalam contoh berikut, yang men-scroll lirik lagu baris demi baris, pemberhentian 0.dp antara 0.42f dan 0.58f membuat baris aktif di tengah tetap tajam, sementara baris di tepi atas dan bawah semakin buram:

@Composable
fun SongListTopBottomBlurExample(lyrics: List<String>) {
    // ...
    LazyColumn(
        state = listState,
        flingBehavior = flingBehavior,
        modifier = Modifier
            .width(330.dp)
            .height(480.dp)
            .padding(horizontal = 24.dp)
            .blur(
                blurRadiusSpec = BlurRadiusSpec.verticalGradient(
                    stops = listOf(
                        BlurStop(fraction = 0.00f, radius = 18.dp), // Heavily blurred top edge
                        BlurStop(fraction = 0.24f, radius = 8.dp), // Soft upper transition
                        BlurStop(fraction = 0.42f, radius = 0.dp), // Sharp center focus starts
                        BlurStop(fraction = 0.58f, radius = 0.dp), // Sharp center focus ends
                        BlurStop(fraction = 0.76f, radius = 8.dp), // Soft lower transition
                        BlurStop(fraction = 1.00f, radius = 18.dp), // Heavily blurred bottom edge
                    ),
                ),
                edgeTreatment = BlurredEdgeTreatment.Unbounded,
            ),
        contentPadding = PaddingValues(vertical = 36.dp),
        verticalArrangement = Arrangement.spacedBy(26.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        // ...
    }
}

Gambar 1. Daftar vertikal baris teks lirik lagu dengan baris atas dan bawah memudar dalam blur, sedangkan baris tengah terlihat jelas.

Memudarkan tepi kiri dan kanan kontrol horizontal

Demikian pula, untuk menarik fokus ke tengah komponen yang dapat di-scroll atau ditarik secara horizontal, seperti pemilih roda, carousel, atau skala pengukuran, terapkan BlurRadiusSpec.horizontalGradient dengan entri BlurStop. Pada timbangan berat yang dapat ditarik berikut, tepi kiri dan kanan menjadi buram, sedangkan garis dan angka di dekat jarum indikator tengah tetap terlihat tajam:

@Composable
fun HorizontalScaleBlurExample() {
    var selectedValue by remember { mutableFloatStateOf(65.0f) }
    val animatedValue by animateFloatAsState(
        targetValue = selectedValue,
        animationSpec = tween(durationMillis = 900, easing = FastOutSlowInEasing),
        label = "horizontalScaleSmoothScroll",
    )
    Column(
        modifier = Modifier
            .width(340.dp)
            .background(Color(0xFF0F172A), RoundedCornerShape(24.dp))
            .padding(vertical = 24.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Text(
            text = "${(animatedValue * 10).roundToInt() / 10.0} kg",
            color = Color.White,
            fontSize = 44.sp,
            fontWeight = FontWeight.Black,
        )
        Spacer(Modifier.height(16.dp))
        Box(
            // ...
        ) {
            // Ruler ticks blurred at the left and right edges, sharp at center
            Canvas(
                modifier = Modifier
                    .fillMaxSize()
                    .blur(
                        blurRadiusSpec = BlurRadiusSpec.horizontalGradient(
                            stops = listOf(
                                BlurStop(fraction = 0.00f, radius = 14.dp),
                                BlurStop(fraction = 0.28f, radius = 0.dp),
                                BlurStop(fraction = 0.72f, radius = 0.dp),
                                BlurStop(fraction = 1.00f, radius = 14.dp),
                            ),
                        ),
                        edgeTreatment = BlurredEdgeTreatment.Unbounded,
                    ),
            ) {
                // Draw horizontal ruler ticks offset by `animatedValue`
                // ...
            }
            // ...
        }
    }
}

Gambar 2. Widget skala penggaris horizontal yang menampilkan tanda centang buram di dekat tepi dan tanda centang tajam di dekat jarum indikator.

Membuat blur diagonal dengan linearGradient

BlurRadiusSpec.linearGradient memvariasikan radius blur di sepanjang segmen garis antara nilai start dan end DpOffset di ruang koordinat lapisan. Di dalam blok lambda Modifier.blur, Anda dapat membaca BlurScope.size untuk menjalankan blur progresif diagonal di seluruh banner besar dari sudut kiri atas ke sudut kanan bawah, sehingga subjek di kiri atas tetap tajam sepenuhnya, sekaligus membuat blur wilayah kanan bawah di belakang teks overlay secara progresif:

@Composable
fun DiagonalLinearBlurCardExample() {
    Box(
        modifier = Modifier
            .wrapContentSize()
            .background(Color(0xFFF8FAFC))
            .padding(10.dp),
    ) {
        Image(
            painter = painterResource(id = R.drawable.dog),
            contentDescription = stringResource(id = R.string.dog_content_description),
            contentScale = ContentScale.Crop,
            modifier = Modifier
                .size(width = 300.dp, height = 380.dp)
                .blur {
                    blurRadiusSpec = BlurRadiusSpec.linearGradient(
                        start = DpOffset(0.dp, 0.dp),
                        end = DpOffset(size.width, size.height),
                        stops = listOf(
                            BlurStop(fraction = 0.00f, radius = 0.dp),
                            BlurStop(fraction = 0.50f, radius = 0.dp),
                            BlurStop(fraction = 0.75f, radius = 14.dp),
                            BlurStop(fraction = 1.00f, radius = 28.dp),
                        ),
                    )
                    edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(24.dp))
                },
        )
        // ...
    }
}

Potret anjing yang tajam di kiri atas dan semakin buram ke arah sudut kanan bawah, dengan teks judul dan subtitel putih tajam yang dihamparkan di area buram.
Gambar 3. Potret anjing yang tajam di kiri atas dan semakin buram ke arah sudut kanan bawah, dengan teks judul dan subtitel putih tajam yang ditempatkan di atas area buram.

Membuat sorotan fokus dengan radialGradient

BlurRadiusSpec.radialGradient menciptakan efek sorotan melingkar dengan memvariasikan radius blur ke luar dari titik fokus. Tentukan center sebagai DpOffset dan fallOffRadius sebagai jarak Dp dari center tempat endRadius tercapai. Jika Anda membiarkan center sebagai DpOffset.Unspecified, gradien akan dipusatkan pada composable, dan jika Anda membiarkan fallOffRadius sebagai Dp.Unspecified, gradien akan ditetapkan secara default ke setengah dimensi lapisan yang lebih kecil.

Misalnya, menerapkan BlurRadiusSpec.radialGradient pada bagian teks dan memperbarui center dari peristiwa tarik pointer memungkinkan pengguna menarik sorotan bacaan melingkar di seluruh paragraf sambil memburamkan teks di sekitarnya, seperti yang ditunjukkan dalam contoh berikut:

@Composable
fun RadialSpotlightTextExample() {
    var touchOffset by remember { mutableStateOf(Offset.Unspecified) }
    Box(
        modifier = Modifier
            .size(width = 330.dp, height = 340.dp)
            .background(Color(0xFF0F172A), RoundedCornerShape(20.dp))
            .pointerInput(Unit) {
                detectDragGestures(
                    onDragStart = { offset -> touchOffset = offset },
                    onDrag = { change, _ -> touchOffset = change.position },
                )
            }
            .blur {
                val spotlightCenter = if (touchOffset.isSpecified) {
                    DpOffset(touchOffset.x.toDp(), touchOffset.y.toDp())
                } else {
                    DpOffset(x = size.width / 2, y = size.height / 2)
                }
                blurRadiusSpec = BlurRadiusSpec.radialGradient(
                    startRadius = 0.dp,
                    endRadius = 10.dp,
                    center = spotlightCenter,
                    fallOffRadius = 215.dp,
                )
                edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(20.dp))
            }
            .padding(22.dp),
    ) {
        // ...
    }
}

Gambar 4. Satu paragraf teks dengan area melingkar berisi teks yang tajam di sekitar kursor yang menjadi semakin buram ke arah tepi lingkaran.

Membuat mask blur kustom dengan shader

Untuk distribusi blur spasial yang kompleks di luar gradien linear atau radial, seperti pola berulang, bentuk arbitrer, atau derau prosedural, teruskan Shader kustom, seperti RuntimeShader AGSL, ke BlurRadiusSpec.shader. Shader mengevaluasi koordinat piksel lapisan dari origin kiri atas, dan saluran alfa sampelnya (0..1) menskalakan maxRadius.

Buat shader dan konfigurasi BlurRadiusSpec.shader sekali menggunakan fungsi composable remember, sehingga satu instance digunakan kembali di seluruh rekomposisi. Compose memanggil blok konfigurasi dengan ukuran lapisan pada setiap penyelesaian, yang merupakan tempat Anda menetapkan uniform yang bergantung pada ukuran:

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
fun ShaderBlurExample() {
    // Creates 6 diagonal frosted-glass bands across the image.
    val maskShader = remember {
        RuntimeShader(
            """
            uniform float2 size;
            half4 main(float2 coord) {
                float2 uv = coord / size;
                float band = smoothstep(0.4, 0.6, fract((uv.x + uv.y) * 3.0));
                return half4(0.0, 0.0, 0.0, half(band));
            }
            """.trimIndent(),
        )
    }
    val maskBlurSpec = remember(maskShader) {
        BlurRadiusSpec.shader(maxRadius = 24.dp) { sizePx ->
            maskShader.setFloatUniform("size", sizePx.width, sizePx.height)
            maskShader
        }
    }
    Image(
        painter = painterResource(id = R.drawable.dog),
        contentDescription = stringResource(id = R.string.dog_content_description),
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .size(150.dp)
            .blur(blurRadiusSpec = maskBlurSpec),
    )
}

Gambar anjing dengan pita blur kaca buram diagonal yang digerakkan oleh masker shader kustom.
Gambar 5. Gambar anjing dengan pita blur kaca buram diagonal yang didorong oleh masker shader kustom.