常見的模糊處理用途

以下各節說明如何結合 Modifier.blur、BlurRadiusSpec、BlurStop 和 BlurScope,用於常見的 UI 模式。如要瞭解這些 API,請參閱「在 Compose 中模糊處理」。

模糊處理捲動或可拖曳內容的邊緣

顯示捲動清單、輪轉介面或挑選器時,您可以使用多停點漸進式模糊處理,柔化容器邊緣附近的項目,同時讓使用中的中心區域保持清晰。

淡出捲動清單的頂端和底部邊緣

如要在項目捲動進出垂直清單的可視區域時,柔化項目邊緣,請將含有 BlurStop 項目的 BlurRadiusSpec.verticalGradient 套用至 LazyColumn。在以下範例中,歌詞會逐行捲動,0.42f 和 0.58f 之間的 0.dp 停靠點會讓中央的活動行保持清晰,而靠近頂端和底端邊緣的行則會逐漸模糊:

@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,
    ) {
        // ...
    }
}

圖 1. 垂直排列的歌詞文字,頂端和底部的文字會模糊淡出,中間的文字則清晰可見。

淡出水平控制項的左右邊緣

同樣地,如要將焦點移至可水平捲動或拖曳的元件中心 (例如滾輪挑選器、輪轉介面或測量刻度),請套用 BlurRadiusSpec.horizontalGradient 和 BlurStop 項目。在下列可拖曳的體重計中,左右邊緣會模糊,而中心指標針附近的刻度和數字則保持完全清晰:

@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`
                // ...
            }
            // ...
        }
    }
}

圖 2. 水平尺規刻度小工具,邊緣附近的刻度標記模糊,指標針附近的刻度標記清晰。

使用 linearGradient 建立對角模糊效果

BlurRadiusSpec.linearGradient 會沿著圖層座標空間中 start 和 end DpOffset 值之間的線段,改變模糊半徑。在 Modifier.blur lambda 區塊中,您可以讀取 BlurScope.size,從左上角到右下角對主頁橫幅套用對角漸進模糊效果,讓左上角的主體保持完全清晰,同時逐漸模糊疊加文字後方的右下角區域:

@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))
                },
        )
        // ...
    }
}

一張狗的肖像照,左上角清晰,右下角逐漸模糊,模糊區域疊加清晰的白色標題和副標題文字。
圖 3. 狗肖像左上角清晰,右下角逐漸模糊,模糊區域疊加清晰的白色標題和副標題文字。

使用 radialGradient 建立焦點精選

BlurRadiusSpec.radialGradient 會從焦點向外改變模糊半徑,建立圓形聚光燈效果。將 center 定義為 DpOffset,並將 fallOffRadius 定義為從 center 到達 endRadius 的 Dp 距離。如果將 center 設為 DpOffset.Unspecified,漸層會以可組合項為中心;如果將 fallOffRadius 設為 Dp.Unspecified,則預設為圖層較小尺寸的一半。

舉例來說,在一段文字上套用 BlurRadiusSpec.radialGradient,並根據指標拖曳事件更新 center,使用者就能在段落中拖曳圓形閱讀聚光燈,同時模糊周圍文字,如以下範例所示:

@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),
    ) {
        // ...
    }
}

圖 4. 一段文字,游標周圍有圓形區域,文字清晰,越靠近圓形邊緣越模糊。

使用著色器建立自訂模糊遮罩

如果空間模糊分布較為複雜,超出線性或放射狀漸層,例如重複圖案、任意形狀或程序雜訊,請將自訂 Shader (例如 AGSL RuntimeShader) 傳遞至 BlurRadiusSpec.shader。著色器會從左上角原點開始,以圖層像素座標進行評估,其取樣的 Alpha 色板 (0..1) 會縮放 maxRadius。

使用 remember 可組合函式建立著色器和 BlurRadiusSpec.shader 設定一次,以便在重組時重複使用單一例項。Compose 會在每次解析時,使用圖層大小叫用設定區塊,您可以在這裡設定與大小相關的統一變數:

@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),
    )
}

狗的圖片,由自訂著色器遮罩驅動,呈現對角線毛玻璃模糊帶。
圖 5. 狗圖片,由自訂著色器遮罩驅動的對角磨砂玻璃模糊帶。