تساهم الظلال في تحسين مظهر واجهة المستخدم، وتشير إلى إمكانية التفاعل معها، وتقدّم ملاحظات فورية للمستخدمين بشأن الإجراءات التي يتخذونها. يوفّر Compose عدة طرق لدمج الظلال في تطبيقك:
Modifier.shadow(): تنشئ هذه الدالة ظلًا مستندًا إلى الارتفاع خلف دالة مركّبة يتوافق مع إرشادات "التصميم المتعدد الأبعاد".Modifier.dropShadow(): تنشئ هذه السمة ظلًا قابلاً للتخصيص يظهر خلف عنصر قابل للإنشاء، ما يجعله يبدو مرتفعًا.Modifier.innerShadow(): تنشئ هذه السمة ظلًا داخل حدود دالة مركّبة، ما يجعله يبدو مضغوطًا على السطح خلفه.
تكون Modifier.shadow() مناسبة لإنشاء ظلال أساسية، بينما يوفّر المعدّلان dropShadow() وinnerShadow() تحكّمًا أكثر دقة في عرض الظلال.
توضّح هذه الصفحة كيفية تنفيذ كل من هذه المعدِّلات، بما في ذلك كيفية تحريك الظلال عند تفاعل المستخدم وكيفية ربط المعدِّلين innerShadow() وdropShadow() لإنشاء ظلال متدرّجة وظلال نيو مورفية وغير ذلك.
إنشاء ظلال أساسية
تنشئ Modifier.shadow() ظلًا أساسيًا يتّبع إرشادات التصميم المتعدد الأبعاد تحاكي مصدر ضوء علويًا. يعتمد عمق الظل على قيمة 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 Expressive.
لتنفيذ تظليل أساسي، أضِف المعدِّل 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() على مستطيل ذي زوايا مستديرةتحريك الظلال عند تفاعل المستخدم
لجعل الظلال تتجاوب مع تفاعلات المستخدمين، يمكنك دمج خصائص الظلال مع واجهات برمجة تطبيقات الرسوم المتحركة في 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(اختصارًا لعبارة "بين") بمدة 400 ملي ثانية ومنحنىEaseInOut، ما يعني أنّ الصورة المتحركة تبدأ ببطء، وتزداد سرعتها في المنتصف، ثم تنخفض في النهاية. - تحدّد هذه السمة
Boxمع سلسلة من دوال التعديل التي تطبّق جميع الخصائص المتحركة لإنشاء العنصر المرئي، بما في ذلك ما يلي:- .
clickable(): هي أداة تعديل تجعلBoxتفاعليًا. .dropShadow(): يتم تطبيق ظلّين خارجيين أولاً، ويتم ربط خصائص اللون والشفافية بالقيم المتحركة (blueDropShadowوما إلى ذلك) لإنشاء المظهر المرتفع الأولي.-
.innerShadow(): يتم رسم ظلّين داخليين فوق الخلفية، وترتبط سماتهما بمجموعة القيم المتحركة الأخرى (innerShadowColor1وما إلى ذلك) وتؤدي إلى ظهور مظهر مسنّن.
- .
النتيجة
إنشاء ظلال متدرّجة
لا تقتصر الظلال على الألوان الثابتة، بل تقبل واجهة برمجة التطبيقات الخاصة بالظلال 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() لإنشاء تأثير
neumorphic:
@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) ) }
إنشاء ظلال بأسلوب الوحشية الجديدة
يعرض نمط Neobrutalist تصميمات مربّعة عالية التباين وألوانًا زاهية وحدودًا سميكة. لإنشاء هذا التأثير، استخدِم 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()المتسلسلة لإنشاء تأثير الحافة المعدنية حول حافة المكوّن.
النتيجة
ينتج مقتطف الرمز البرمجي السابق ما يلي: