הצללות משפרות את המראה של ממשק המשתמש, מציינות למשתמשים שיש אפשרות לאינטראקציה ומספקות משוב מיידי על פעולות המשתמשים. ב-Compose יש כמה דרכים לשלב צללים באפליקציה:
-
Modifier.shadow(): יוצרת צל מאחורי רכיב שאפשר להרכיב, בהתאם להנחיות של Material Design. -
Modifier.dropShadow(): יוצרת צל שאפשר להתאים אישית שמופיע מאחורי רכיב שאפשר להרכיב, וגורם לו להיראות מוגבה. -
Modifier.innerShadow(): יוצר צל בתוך הגבולות של רכיב שאפשר להוסיף לו רכיבים אחרים, כך שהוא נראה כאילו הוא מוטבע במשטח שמאחוריו.
האפשרות Modifier.shadow() מתאימה ליצירת צללים בסיסיים, בעוד שהאפשרויות dropShadow() ו-innerShadow() מאפשרות שליטה מדויקת יותר על עיבוד הצללים.
בדף הזה מוסבר איך להטמיע כל אחד מהמשנים האלה, כולל איך להנפיש צללים כשמשתמשים יוצרים אינטראקציה ואיך לשרשר את המשנים innerShadow() ו-dropShadow() כדי ליצור צללים עם מעברי צבע, צללים ניאומורפיים ועוד.
יצירת צללים בסיסיים
Modifier.shadow() יוצר צל בסיסי לפי ההנחיות של Material Design, שמדמה מקור אור מלמעלה. עומק הצל מבוסס על ערך elevation, והצל המוטל נחתך לצורה של הרכיב הניתן להרכבה.
@Composable fun ElevationBasedShadow() { Box( modifier = Modifier.aspectRatio(1f).fillMaxSize(), contentAlignment = Alignment.Center ) { Box( Modifier .size(100.dp, 100.dp) .shadow(10.dp, RectangleShape) .background(Color.White) ) } }
Modifier.shadow().הטמעת הטלת צללית
אפשר להשתמש בשינוי dropShadow() כדי ליצור צל מדויק מאחורי התוכן, וכך ליצור מראה של אלמנט מוגבה.
אפשר לשלוט בהיבטים החשובים הבאים באמצעות הפרמטר Shadow:
radius: מגדיר את הרכות והפיזור של הטשטוש.-
color: הגדרת הצבע של הגוון. -
offset: ממקם את הגיאומטריה של הצל לאורך הצירים x ו-y. -
spread: קובע את ההתרחבות או ההתכווצות של הצורה של הצל.
בנוסף, הפרמטר shape מגדיר את הצורה הכוללת של הצל. אפשר להשתמש בכל צורה גיאומטרית מחבילת androidx.compose.foundation.shape, וגם בצורות המיוחדות של Material.
כדי להטמיע הטלת צללית בסיסית, מוסיפים את המשנה dropShadow() לשרשרת הרכיבים הניתנים להרכבה, ומציינים את הרדיוס, הצבע וההתפשטות. שימו לב שהרקע purpleColor שמופיע מעל הצל מצויר אחרי המאפיין dropShadow():
@Composable fun SimpleDropShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(300.dp) .dropShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 6.dp, color = Color(0x40000000), offset = DpOffset(x = 4.dp, 4.dp) ) ) .align(Alignment.Center) .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) ) { Text( "Drop Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }
מידע חשוב על הקוד
- המשנה
dropShadow()מוחל עלBoxהפנימי. לצללית יש את המאפיינים הבאים:- צורה של מלבן מעוגל (
RoundedCornerShape(20.dp)) - רדיוס הטשטוש הוא
10.dp, כך שהקצוות רכים ומפוזרים - ערך של
6.dp, שמרחיב את גודל הצל והופך אותו לגדול יותר מהתיבה שיוצרת אותו - ערך אלפא של
0.5f, כך שהצל יהיה שקוף למחצה
- צורה של מלבן מעוגל (
- אחרי שמגדירים את הצל,המשנה
background()מוחל.- הסמל
Boxמלא בצבע לבן. - הרקע נחתך לצורה של מלבן מעוגל כמו הצל.
- הסמל
התוצאה
הטמעה של צללים פנימיים
כדי ליצור אפקט הפוך ל-dropShadow(), משתמשים ב-Modifier.innerShadow(), שיוצר אשליה של שקע או של אלמנט שקוע או מוטבע במשטח שמתחתיו.
הסדר חשוב כשיוצרים צללים פנימיים. המשנה innerShadow() מתייחס לחלק העליון של התוכן. כדי לוודא שהצל נראה, בדרך כלל מבצעים את השלבים הבאים:
- משרבטים את תוכן הרקע.
- כדי ליצור את המראה הקעור, מוסיפים את המשנה
innerShadow().
אם התג innerShadow() ממוקם לפני הרקע, הרקע מצויר מעל הצל ומסתיר אותו לחלוטין.
בדוגמה הבאה מוצג יישום של innerShadow() ב-RoundedCornerShape:
@Composable fun SimpleInnerShadowUsage() { Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) // note that the background needs to be defined before defining the inner shadow .background( color = Color.White, shape = RoundedCornerShape(20.dp) ) .innerShadow( shape = RoundedCornerShape(20.dp), shadow = Shadow( radius = 10.dp, spread = 2.dp, color = Color(0x40000000), offset = DpOffset(x = 6.dp, 7.dp) ) ) ) { Text( "Inner Shadow", modifier = Modifier.align(Alignment.Center), fontSize = 32.sp ) } } }
Modifier.innerShadow() במלבן עם פינות מעוגלות.הנפשת הצללים באינטראקציה עם המשתמש
כדי שהצללים יגיבו לאינטראקציות של המשתמשים, אפשר לשלב מאפייני צל עם ממשקי ה-API של אנימציה ב-Compose. לדוגמה, כשמשתמש לוחץ על לחצן, הצל יכול להשתנות כדי לספק משוב חזותי מיידי.
הקוד הבא יוצר אפקט של לחיצה עם צל (האשליה שהמשטח נלחץ כלפי מטה לתוך המסך):
@Composable fun AnimatedColoredShadows() { SnippetsTheme { Box(Modifier.fillMaxSize()) { val interactionSource = remember { MutableInteractionSource() } val isPressed by interactionSource.collectIsPressedAsState() // Create transition with pressed state val transition = updateTransition( targetState = isPressed, label = "button_press_transition" ) fun <T> buttonPressAnimation() = tween<T>( durationMillis = 400, easing = EaseInOut ) // Animate all properties using the transition val shadowAlpha by transition.animateFloat( label = "shadow_alpha", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) 0f else 1f } // ... val blueDropShadow by transition.animateColor( label = "shadow_color", transitionSpec = { buttonPressAnimation() } ) { pressed -> if (pressed) Color.Transparent else blueDropShadowColor } // ... Box( Modifier .clickable( interactionSource, indication = null ) { // ** ...... **// } .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = blueDropShadow, offset = DpOffset(x = 0.dp, -(2).dp), alpha = shadowAlpha ) ) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = 0.dp, color = darkBlueDropShadow, offset = DpOffset(x = 2.dp, 6.dp), alpha = shadowAlpha ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color(0xFFFFFFFF), shape = RoundedCornerShape(70.dp) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 8.dp, spread = 4.dp, color = innerShadowColor2, offset = DpOffset(x = 4.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 20.dp, spread = 4.dp, color = innerShadowColor1, offset = DpOffset(x = 4.dp, 0.dp), alpha = innerShadowAlpha ) ) ) { Text( "Animated Shadows", // ... ) } } } }
מידע חשוב על הקוד
- מצהירים על מצבי ההתחלה והסיום של הפרמטרים להנפשה בלחיצה באמצעות
transition.animateColorו-transition.animateFloat. - משתמש ב-
updateTransitionומספק לו אתtargetState (targetState = isPressed)שנבחר כדי לוודא שכל האנימציות מסונכרנות. בכל פעם ש-isPressedמשתנה, אובייקט המעבר מנהל באופן אוטומטי את האנימציה של כל נכסי הצאצאים מהערכים הנוכחיים שלהם לערכי היעד החדשים. - המאפיין הזה מגדיר את
buttonPressAnimationהמפרט, שקובע את התזמון ואת ההאצה של המעבר. הוא מצייןtween(קיצור של in-between) עם משך של 400 אלפיות שנייה ועקומתEaseInOut, כלומר האנימציה מתחילה לאט, מאיצה באמצע ומאטה בסוף. - הגדרת
Boxעם שרשרת של פונקציות לשינוי מאפיינים שחלות על כל המאפיינים של האנימציה כדי ליצור את האלמנט החזותי, כולל המאפיינים הבאים:- .
clickable(): ערך מקדם שגורם ל-Boxלהיות אינטראקטיבי. -
.dropShadow(): קודם כל מוחלות שתי צלליות חיצוניות. מאפייני הצבע והאלפא שלהם מקושרים לערכים המונפשים (blueDropShadowוכו') ויוצרים את המראה המוגבה הראשוני. -
.innerShadow(): שתי צלליות פנימיות מצוירות מעל הרקע. המאפיינים שלהם מקושרים לקבוצה השנייה של ערכים מונפשים (innerShadowColor1וכו') ויוצרים את המראה המוזח.
- .
התוצאה
יצירת צללים עם מעברי צבע
הצללות לא מוגבלות לצבעים אחידים. Shadow API מקבל Brush, שמאפשר ליצור צללים עם מעברי צבע.
Box( modifier = Modifier .width(240.dp) .height(200.dp) .dropShadow( shape = RoundedCornerShape(70.dp), shadow = Shadow( radius = 10.dp, spread = animatedSpread.dp, brush = Brush.sweepGradient( colors ), offset = DpOffset(x = 0.dp, y = 0.dp), alpha = animatedAlpha ) ) .clip(RoundedCornerShape(70.dp)) .background(Color(0xEDFFFFFF)), contentAlignment = Alignment.Center ) { Text( text = breathingText, color = Color.Black, style = MaterialTheme.typography.bodyLarge ) }
מידע חשוב על הקוד
-
dropShadow()מוסיף צל מאחורי התיבה. -
brush = Brush.sweepGradient(colors)צובע את הצל בהדרגה, עם סיבוב ברשימה שלcolorsמוגדרים מראש, ויוצר אפקט של קשת בענן.
התוצאה
אפשר להשתמש במברשת כצל כדי ליצור מעבר הדרגתי dropShadow() עם אנימציה של 'נשימה':
שילוב צללים
אפשר לשלב בין התנאים dropShadow() ו-innerShadow() ולהוסיף אותם אחד על השני כדי ליצור מגוון אפקטים. בקטעים הבאים מוסבר איך ליצור צללים ניאומורפיים, ניאו-ברוטליסטיים וריאליסטיים באמצעות הטכניקה הזו.
יצירת צללים בסגנון ניאומורפיזם
צללים ניאומורפיים מאופיינים במראה רך שנוצר באופן אורגני מהרקע. כדי ליצור צללים ניאומורפיים:
- להשתמש ברכיב עם צבעים זהים לצבעי הרקע שלו.
- החלת שתי צלליות חלשות ומנוגדות: צללית בהירה בפינה אחת וצללית כהה בפינה הנגדית.
בקטע הקוד הבא מוסיפים שני משני dropShadow() כדי ליצור את האפקט הניאומורפי:
@Composable fun NeumorphicRaisedButton( shape: RoundedCornerShape = RoundedCornerShape(30.dp) ) { val bgColor = Color(0xFFe0e0e0) val lightShadow = Color(0xFFFFFFFF) val darkShadow = Color(0xFFb1b1b1) val upperOffset = -10.dp val lowerOffset = 10.dp val radius = 15.dp val spread = 0.dp Box( modifier = Modifier .fillMaxSize() .background(bgColor) .wrapContentSize(Alignment.Center) .size(240.dp) .dropShadow( shape, shadow = Shadow( radius = radius, color = lightShadow, spread = spread, offset = DpOffset(upperOffset, upperOffset) ), ) .dropShadow( shape, shadow = Shadow( radius = radius, color = darkShadow, spread = spread, offset = DpOffset(lowerOffset, lowerOffset) ), ) .background(bgColor, shape) ) }
יצירת צללים בסגנון ניאו-ברוטליסטי
הסגנון הנאו-ברוטליסטי מתאפיין בניגודיות גבוהה, בפריסות מגושמות, בצבעים עזים ובגבולות עבים. כדי ליצור את האפקט הזה, משתמשים ב-dropShadow() עם אפס טשטוש והזחה ברורה, כמו שמוצג בקטע הקוד הבא:
@Composable fun NeoBrutalShadows() { SnippetsTheme { val dropShadowColor = Color(0xFF007AFF) val borderColor = Color(0xFFFF2D55) Box(Modifier.fillMaxSize()) { Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(0.dp), shadow = Shadow( radius = 0.dp, spread = 0.dp, color = dropShadowColor, offset = DpOffset(x = 8.dp, 8.dp) ) ) .border( 8.dp, borderColor ) .background( color = Color.White, shape = RoundedCornerShape(0.dp) ) ) { Text( "Neobrutal Shadows", modifier = Modifier.align(Alignment.Center), style = MaterialTheme.typography.bodyMedium ) } } } }
יצירת צללים מציאותיים
צללים מציאותיים מחקים צללים בעולם הפיזי – הם נראים כאילו הם מוארים על ידי מקור אור ראשי, וכתוצאה מכך נוצר גם צל ישיר וגם צל מפוזר יותר. אפשר להשתמש בכמה מקרים של dropShadow() ו-innerShadow() עם מאפיינים שונים כדי ליצור מחדש אפקטים של צל שנראים מציאותיים, כמו בדוגמה הבאה:
@Composable fun RealisticShadows() { Box(Modifier.fillMaxSize()) { val dropShadowColor1 = Color(0xB3000000) val dropShadowColor2 = Color(0x66000000) val innerShadowColor1 = Color(0xCC000000) val innerShadowColor2 = Color(0xFF050505) val innerShadowColor3 = Color(0x40FFFFFF) val innerShadowColor4 = Color(0x1A050505) Box( Modifier .width(300.dp) .height(200.dp) .align(Alignment.Center) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 40.dp, spread = 0.dp, color = dropShadowColor1, offset = DpOffset(x = 2.dp, 8.dp) ) ) .dropShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 0.dp, color = dropShadowColor2, offset = DpOffset(x = 0.dp, 4.dp) ) ) // note that the background needs to be defined before defining the inner shadow .background( color = Color.Black, shape = RoundedCornerShape(100.dp) ) // // .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 3.dp, color = innerShadowColor1, offset = DpOffset(x = 6.dp, 6.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 4.dp, spread = 1.dp, color = Color.White, offset = DpOffset(x = 5.dp, 5.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 12.dp, spread = 5.dp, color = innerShadowColor2, offset = DpOffset(x = (-3).dp, (-12).dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 10.dp, color = innerShadowColor3, offset = DpOffset(x = 0.dp, 0.dp) ) ) .innerShadow( shape = RoundedCornerShape(100.dp), shadow = Shadow( radius = 3.dp, spread = 9.dp, color = innerShadowColor4, offset = DpOffset(x = 1.dp, 1.dp) ) ) ) { Text( "Realistic Shadows", modifier = Modifier.align(Alignment.Center), fontSize = 24.sp, color = Color.White ) } } }
מידע חשוב על הקוד
- שני משני
dropShadow()מאפיינים שונים מחוברים ומופעלים, ואחריהם מופעל משניbackground(). - השימוש במגדירי
innerShadow()שרשרת מאפשר ליצור את אפקט המסגרת המתכתית סביב קצה הרכיב.
התוצאה
קטע הקוד הקודם יוצר את הפלט הבא: