以下部分介绍了如何将 Modifier.blur、BlurRadiusSpec、BlurStop 和 BlurScope 组合用于常见的界面模式。如需了解这些 API,请参阅在 Compose 中实现模糊效果。
模糊滚动或可拖动内容的边缘
在显示滚动列表、轮播界面或选择器时,您可以使用多停靠点渐进式模糊处理来柔化容器边缘附近的项,同时使活跃的中心区域保持清晰对焦。
淡化滚动列表的顶部和底部边缘
如需在列表项滚动进入和滚出垂直列表的视口时柔化它们,请将具有 BlurStop 条目的 BlurRadiusSpec.verticalGradient 应用于 LazyColumn。在以下示例中,歌词会逐行滚动,0.dp 停止点位于 0.42f 和 0.58f 之间,使活动行在中心保持清晰,而靠近顶部和底部边缘的行则逐渐模糊:
@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, ) { // ... } }
淡化水平控件的左右边缘
同样,如需将焦点吸引到可水平滚动或可拖动的组件(例如滚轮选择器、轮播界面或测量刻度)的中心,请应用具有 BlurStop 条目的 BlurRadiusSpec.horizontalGradient。在以下可拖动的体重秤中,左右边缘会柔化,而中心指示针附近的刻度和数字则保持完全清晰:
@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` // ... } // ... } } }
使用 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)) }, ) // ... } }
使用 radialGradient 创建焦点聚光灯
BlurRadiusSpec.radialGradient 通过从焦点向外改变模糊半径来创建圆形聚光灯效果。将 center 定义为 DpOffset,将 fallOffRadius 定义为达到 endRadius 时与 center 的 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), ) { // ... } }
使用着色器创建自定义模糊遮罩
对于超出线性或径向渐变的复杂空间模糊分布(例如重复图案、任意形状或程序化噪声),请将自定义 Shader(例如 AGSL RuntimeShader)传递给 BlurRadiusSpec.shader。着色器会根据左上角原点的图层像素坐标进行求值,其采样的 Alpha 通道 (0..1) 会缩放 maxRadius。
使用 remember 可组合函数创建着色器和 BlurRadiusSpec.shader 配置一次,以便在重组过程中重复使用单个实例。Compose 会在每次解析时调用具有图层大小的配置块,您可以在其中设置与大小相关的 uniform:
@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), ) }