Compose में शैडो जोड़ना

शैडो, आपके यूज़र इंटरफ़ेस (यूआई) को विज़ुअल तौर पर बेहतर बनाते हैं. साथ ही, उपयोगकर्ताओं को इंटरैक्टिविटी के बारे में बताते हैं और उनकी कार्रवाइयों पर तुरंत फ़ीडबैक देते हैं. 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 पैकेज की किसी भी ज्यामिति के साथ-साथ, मटीरियल एक्सप्रेसिव शेप का भी इस्तेमाल कर सकता है.

बेसिक ड्रॉप शैडो लागू करने के लिए, अपनी कंपोज़ेबल चेन में 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() मॉडिफ़ायर, कॉन्टेंट के सबसे ऊपर ड्रॉ होता है. शैडो को दिखाने के लिए, आम तौर पर यह तरीका अपनाया जाता है:

  1. बैकग्राउंड कॉन्टेंट ड्रॉ करें.
  2. कॉन्केव लुक बनाने के लिए, innerShadow() मॉडिफ़ायर लागू करें.

अगर innerShadow() को बैकग्राउंड से पहले रखा जाता है, तो बैकग्राउंड को शैडो के ऊपर ड्रॉ किया जाता है. इससे शैडो पूरी तरह से छिप जाती है.

नीचे दिए गए उदाहरण में, RoundedCornerShape पर innerShadow() का इस्तेमाल दिखाया गया है:

@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 की खास जानकारी देता है. इससे ट्रांज़िशन के समय और ईज़िंग को कंट्रोल किया जाता है. यह 400 मिलीसेकंड की अवधि और EaseInOut कर्व के साथ tween (इन-बिटवीन का छोटा नाम) तय करता है. इसका मतलब है कि ऐनिमेशन धीरे-धीरे शुरू होता है, बीच में तेज़ होता है, और आखिर में धीमा हो जाता है.
  • यह मॉडिफ़ायर फ़ंक्शन की चेन के साथ 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() मॉडिफ़ायर को जोड़कर और लेयर करके, कई तरह के इफ़ेक्ट बनाए जा सकते हैं. यहां दिए गए सेक्शन में, इस तकनीक का इस्तेमाल करके न्यूमॉर्फ़िक, नियोब्रूटलिस्ट, और रियलिस्टिक शैडो बनाने का तरीका बताया गया है.

न्यूमॉर्फ़िक शैडो बनाना

न्यूमॉर्फ़िक शैडो की खासियत यह है कि यह बैकग्राउंड से नैचुरल तरीके से उभरती है और सॉफ़्ट दिखती है. न्यूमॉर्फ़िक शैडो बनाने के लिए, यह तरीका अपनाएं:

  1. ऐसे एलिमेंट का इस्तेमाल करें जिसका रंग उसके बैकग्राउंड से मेल खाता हो.
  2. दो हल्की और विपरीत ड्रॉप शैडो लागू करें: एक कोने पर हल्की शैडो और विपरीत कोने पर गहरी शैडो.

यहां दिए गए स्निपेट में, न्यूमॉर्फ़िक इफ़ेक्ट बनाने के लिए, दो 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() मॉडिफ़ायर लागू किए जाते हैं.

नतीजा

पिछले कोड स्निपेट से यह नतीजा मिलता है:

काले रंग के गोल आकार के चारों ओर सफ़ेद रंग की असली दिखने वाली शैडो.
आठवीं इमेज. असल लगने वाला शैडो इफ़ेक्ट.