As seções a seguir mostram como combinar Modifier.blur,
BlurRadiusSpec, BlurStop e BlurScope para padrões comuns de
interface. Para uma introdução a essas APIs, consulte Desfoque no Compose.
Desfocar as bordas do conteúdo rolável ou arrastável
Ao mostrar uma lista de rolagem, um carrossel ou um seletor, use desfoques progressivos com várias paradas para suavizar os itens perto das bordas do contêiner, mantendo a região central ativa em foco nítido.
Esmaecer as bordas de cima e de baixo de uma lista rolável
Para suavizar os itens conforme eles entram e saem da janela de visualização de uma lista vertical,
aplique BlurRadiusSpec.verticalGradient com entradas BlurStop ao
LazyColumn. No exemplo a seguir, que rola a letra de uma música linha por linha,
as paradas de 0.dp entre 0.42f e 0.58f mantêm a linha ativa no centro
nítida, enquanto as linhas nas bordas de cima e de baixo ficam progressivamente desfocadas:
@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, ) { // ... } }
Esmaecer as bordas esquerda e direita de um controle horizontal
Da mesma forma, para chamar a atenção para o centro de um componente rolável ou
arrastável horizontalmente, como um seletor de roda, carrossel ou escala de medição,
aplique BlurRadiusSpec.horizontalGradient com entradas BlurStop. Na
balança de peso arrastável a seguir, as bordas esquerda e direita ficam mais suaves, enquanto os
traços e números perto do ponteiro indicador central permanecem completamente nítidos:
@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` // ... } // ... } } }
Criar um desfoque diagonal com linearGradient
BlurRadiusSpec.linearGradient varia o raio de desfoque ao longo de um segmento de linha
entre os valores start e end DpOffset no espaço de coordenadas da camada.
No bloco lambda Modifier.blur, é possível ler BlurScope.size para executar um
desfoque progressivo diagonal em uma imagem principal, do canto superior esquerdo ao
canto inferior direito, mantendo o objeto no canto superior esquerdo completamente nítido e
desfocando progressivamente a região inferior direita atrás do texto sobreposto:
@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)) }, ) // ... } }
Criar um destaque de foco com radialGradient
BlurRadiusSpec.radialGradient cria um efeito de destaque circular variando
o raio de desfoque para fora de um ponto focal. Defina center como um DpOffset e
fallOffRadius como a distância Dp de center em que endRadius é
alcançado. Se você deixar center como DpOffset.Unspecified, o gradiente será
centralizado no elemento combinável. Se você deixar fallOffRadius como
Dp.Unspecified, o padrão será metade da menor dimensão da camada.
Por exemplo, aplicar BlurRadiusSpec.radialGradient sobre um trecho de texto
e atualizar center de eventos de arrastar o ponteiro permite que o usuário arraste um destaque de leitura
circular pelo parágrafo enquanto desfoca o texto ao redor, como
mostrado no exemplo a seguir:
@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), ) { // ... } }
Criar uma máscara de desfoque personalizada com um sombreador
Para distribuições complexas de desfoque espacial além de gradientes lineares ou radiais, como
padrões repetidos, formas arbitrárias ou ruído procedural, transmita um
Shader personalizado, como um AGSL RuntimeShader, para
BlurRadiusSpec.shader. O sombreador é avaliado em coordenadas de pixel de camada da
origem superior esquerda, e o canal Alfa de amostra (0..1) dimensiona maxRadius.
Crie o sombreador e a configuração de BlurRadiusSpec.shader uma vez usando a
função combinável remember para que uma única instância seja reutilizada em
várias recomposições. O Compose invoca o bloco de configuração com o tamanho da camada em
cada resolução, que é onde você define uniformes dependentes de tamanho:
@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), ) }