בקטעים הבאים מוסבר איך לשלב את Modifier.blur, BlurRadiusSpec, BlurStop ו-BlurScope בתבניות נפוצות של ממשק המשתמש. במאמר טשטוש ב-Compose מוסבר על ממשקי ה-API האלה.
טשטוש הקצוות של תוכן שניתן לגלול או לגרור
כשמציגים רשימה נגללת, קרוסלה או בורר, אפשר להשתמש בטשטוש הדרגתי עם כמה נקודות עצירה כדי לרכך פריטים ליד קצוות המאגר, תוך שמירה על מיקוד חד באזור המרכזי הפעיל.
הקצוות העליונים והתחתונים של רשימה נגללת יתעמעמו
כדי לרכך את הפריטים כשהם נגללים לתוך אזור התצוגה של רשימה אנכית ומחוצה לו, צריך להחיל את BlurRadiusSpec.verticalGradient עם רשומות BlurStop על 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, ) { // ... } }
הבהרה של הקצוות השמאלי והימני של פקד אופקי
באופן דומה, כדי למשוך את תשומת הלב למרכז של רכיב שניתן לגלול או לגרור אופקית, כמו בורר גלגל, קרוסלה או סולם מדידה, צריך להחיל את 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` // ... } // ... } } }
יצירת טשטוש אלכסוני באמצעות linearGradient
הפונקציה BlurRadiusSpec.linearGradient משנה את רדיוס הטשטוש לאורך קטע קו בין הערכים start ו-end DpOffset במרחב הקואורדינטות של השכבה.
בתוך בלוק הלמבדה Modifier.blur, אפשר לקרוא את 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 בתור המרחק Dp מ-center שבו מגיעים אל endRadius. אם משאירים את center בתור DpOffset.Unspecified, השיפוע ממורכז ב-composable, ואם משאירים את 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. ההצללה מחושבת בקואורדינטות של פיקסלים בשכבה, החל מהפינה השמאלית העליונה, וערוץ האלפא שנדגם (0..1) משנה את קנה המידה של maxRadius.
יוצרים את ה-shader ואת ההגדרה של BlurRadiusSpec.shader פעם אחת באמצעות הפונקציה הקומפוזבילית remember, כדי שמופע אחד ישמש שוב ושוב בהרכבות מחדש. 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), ) }