Les sections suivantes expliquent comment combiner Modifier.blur, BlurRadiusSpec, BlurStop et BlurScope pour les modèles d'UI courants. Pour en savoir plus sur ces API, consultez Floutage dans Compose.
Flouter les bords du contenu défilant ou déplaçable
Lorsque vous affichez une liste déroulante, un carrousel ou un sélecteur, vous pouvez utiliser des flous progressifs à plusieurs arrêts pour adoucir les éléments situés près des bords du conteneur, tout en gardant la région centrale active bien nette.
Estomper les bords supérieur et inférieur d'une liste déroulante
Pour adoucir les éléments lorsqu'ils défilent dans et hors de la fenêtre d'affichage d'une liste verticale, appliquez BlurRadiusSpec.verticalGradient avec des entrées BlurStop à LazyColumn. Dans l'exemple suivant, qui fait défiler les paroles d'une chanson ligne par ligne, les points d'arrêt 0.dp entre 0.42f et 0.58f maintiennent la ligne active au centre, tandis que les lignes vers les bords supérieur et inférieur s'estompent progressivement :
@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, ) { // ... } }
Estomper les bords gauche et droit d'une commande horizontale
De même, pour attirer l'attention sur le centre d'un composant défilant ou déplaçable horizontalement, comme un sélecteur de roue, un carrousel ou une échelle de mesure, appliquez BlurRadiusSpec.horizontalGradient avec des entrées BlurStop. Dans l'échelle de poids déplaçable suivante, les bords gauche et droit sont adoucis, tandis que les graduations et les chiffres situés près de l'aiguille de l'indicateur central restent parfaitement nets :
@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` // ... } // ... } } }
Créer un flou diagonal avec linearGradient
BlurRadiusSpec.linearGradient fait varier le rayon de flou le long d'un segment de ligne entre les valeurs start et end DpOffset dans l'espace de coordonnées du calque.
Dans le bloc lambda Modifier.blur, vous pouvez lire BlurScope.size pour exécuter un flou progressif diagonal sur une image héros, de l'angle supérieur gauche à l'angle inférieur droit, en gardant le sujet en haut à gauche complètement net tout en floutant progressivement la zone inférieure droite derrière le texte superposé :
@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)) }, ) // ... } }
Créer un spotlight de mise au point avec radialGradient
BlurRadiusSpec.radialGradient crée un effet de projecteur circulaire en faisant varier le rayon de flou vers l'extérieur à partir d'un point focal. Définissez center comme DpOffset et fallOffRadius comme la distance Dp par rapport à center à laquelle endRadius est atteint. Si vous laissez center sur DpOffset.Unspecified, le dégradé est centré sur le composable. Si vous laissez fallOffRadius sur Dp.Unspecified, la valeur par défaut est la moitié de la plus petite dimension du calque.
Par exemple, l'application de BlurRadiusSpec.radialGradient sur un passage de texte et la mise à jour de center à partir d'événements de déplacement du pointeur permettent à l'utilisateur de faire glisser un projecteur de lecture circulaire sur le paragraphe tout en floutant le texte environnant, comme illustré dans l'exemple suivant :
@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), ) { // ... } }
Créer un masque de flou personnalisé avec un nuanceur
Pour les distributions de flou spatial complexes au-delà des dégradés linéaires ou radiaux, comme les motifs répétés, les formes arbitraires ou le bruit procédural, transmettez un Shader personnalisé, tel qu'un RuntimeShader AGSL, à BlurRadiusSpec.shader. Le nuanceur évalue les coordonnées des pixels du calque à partir de l'origine en haut à gauche, et son canal alpha échantillonné (0..1) met à l'échelle maxRadius.
Créez le nuanceur et la configuration BlurRadiusSpec.shader une seule fois à l'aide de la fonction composable remember, afin qu'une seule instance soit réutilisée dans les recompositions. Compose appelle le bloc de configuration avec la taille de la couche à chaque résolution, ce qui vous permet de définir des uniformes dépendant de la taille :
@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), ) }