تدرّجات الشبكة المتداخلة

تنشئ التدرجات الشبكية انتقالات لونية معقدة ومتعددة الاتجاهات باستخدام شبكة ثنائية الأبعاد من الرقع. على عكس التدرجات الخطية أو الشعاعية، فإن تدرجات الشبكة بسلاسة تتداخل الألوان عبر شبكة ثنائية الأبعاد. استخدِم تدرّجات شبكية بدلاً من تجميع عدّة فُرش radialGradient أو linearGradient عند إنشاء خلفيات انسيابية أو طبيعية أو متعددة النقاط في واجهة المستخدم.

مثال على تدرج شبكي مع عرض نقاط التدرج الشبكي الحالية.
الشكل 1 مثال على تدرج شبكي مع عرض نقاط التدرج الشبكي الحالية

المفاهيم الرئيسية

لإنشاء تدرج شبكي، حدِّد أبعاد الشبكة والرؤوس وانتقالات الألوان بين النقاط:

  • أبعاد الشبكة: يتم تقسيم الشبكة إلى أجزاء على طول المحورَين العمودي والأفقي. تحتوي شبكة من rows وcolumns على (الصفوف+1)×(الأعمدة+1) من الرؤوس. على سبيل المثال، تتكوّن الشبكة 1×1 من 4 رؤوس تشكّل رقعة واحدة.
  • الإحداثيات التي تمت تسويتها: تستخدم جميع مواضع الرؤوس نظام إحداثيات تمت تسويته، حيث يمثّل (0f, 0f) أعلى يمين حدود الرسم ويمثّل (1f, 1f) أسفل يسار حدود الرسم.
  • نقاط التحكّم في منحنى بيزييه (المماسات): يحتوي كل رأس على ما يصل إلى أربع نقاط تحكّم اختيارية في منحنى بيزييه. تحدّد هذه المماسات انحناء الحافة بين الرؤوس المجاورة. إذا كنت تستخدم Offset.Unspecified، يستنتج Compose المماسات لضمان الانتقال السلس بين الأجزاء. وكل خلية في الشبكة تتكون من 4 رؤوس مع نقاط التحكّم الخاصة بها تنشئ رقعة بيزييه.
  • استكمال الألوان: يحسب إطار العمل الألوان بين الرؤوس الرئيسية. اضبط hasBicubicColor على true من أجل استيفاء Catmull-Rom للحصول على تغييرات أكثر سلاسة في الألوان، أو false للاستيفاء الثنائي الخطي.

الرسم باستخدام "MeshGradientPainter"

في Jetpack Compose، استخدِم MeshGradientPainter لعرض تدرج شبكي. يرسم MeshGradientPainter على اللوحة.

إنشاء تدرج شبكي بسيط

لإنشاء تدرج شبكي ثابت أساسي، يمكنك إعداد MeshGradientPainter من خلال تحديد أبعاده واستخدام دالة setVertex داخل مجموعة الإعدادات لتحديد مواضع نقاط الزوايا وتعيين ألوان لها.

val rows = 1
val columns = 1

val gradientPainter = remember {
    MeshGradientPainter(rows, columns) {
        // Parameters: row, column, position, color
        setVertex(0, 0, Offset(0f, 0f), Color.Red)     // Top-Left
        setVertex(0, 1, Offset(1f, 0f), Color.Blue)    // Top-Right
        setVertex(1, 0, Offset(0f, 1f), Color.Green)   // Bottom-Left
        setVertex(1, 1, Offset(1f, 1f), Color.Yellow)  // Bottom-Right
    }
}

Box(
    modifier = modifier
        .aspectRatio(16/9f)
        .fillMaxWidth()
        .paint(gradientPainter)
)

تدرّج شبكي أساسي مع 4 ألوان محدّدة في كل زاوية
الشكل 2 تدرج شبكي أساسي بأربعة ألوان، مع ضبط كل زاوية على أحد الألوان الأربعة.

استخدام نقاط تحكّم محدّدة في منحنى بيزييه

يعالج منشئ الشبكة تلقائيًا العمليات الحسابية المعقّدة للحفاظ على سلاسة الانتقالات في الشبكة. ومع ذلك، يمكنك تخصيص المماسات بشكل صريح على أي رأس واحد إذا كنت تريد دفع أقسام ألوان معينة أو سحبها أو ضغطها بشكل انتقائي.

يتم قياس إزاحات عناصر التحكّم بالنسبة إلى موضع رأس المضيف.

val customTangentPainter = remember {
    MeshGradientPainter(rows = 1, columns = 1) {
        // Tweak the top-left vertex to curve outwards to the right and bottom
        setVertex(
            row = 0,
            column = 0,
            position = Offset(0f, 0f),
            color = Color.Magenta,
            rightControlPoint = Offset(0.4f, 0.1f),
            bottomControlPoint = Offset(0.1f, 0.4f)
        )

        // Other points can remain unspecified to use default inferred fallback tangents
        setVertex(0, 1, Offset(1f, 0f), Color.Cyan)
        setVertex(1, 0, Offset(0f, 1f), Color.Blue)
        setVertex(1, 1, Offset(1f, 1f), Color.Black)
    }
}
Box(
    modifier = modifier
        .aspectRatio(16/9f)
        .fillMaxWidth()
        .paint(customTangentPainter)
)

تدرج شبكي مع نقطة منحنية في أعلى اليمين
الشكل 3 يمكنك تقويس الرأس في أعلى يمين الشاشة باستخدام نقطة تحكّم بيزييه.

إنشاء شبكات متقدّمة

يعرض هذا المثال شبكة من 3 في 3، ما يعني أنّ هناك 16 نقطة يجب تحديدها، مع ضبط النقاط الوسطى بإزاحات مختلفة:

val points = remember {
    listOf(
        Offset(0.0f, 0.0f), Offset(0.3f, 0.0f), Offset(0.7f, 0.0f), Offset(1.0f, 0.0f),
        Offset(0.0f, 0.3f), Offset(0.2f, 0.4f), Offset(0.7f, 0.2f), Offset(1.0f, 0.3f),
        Offset(0.0f, 0.7f), Offset(0.3f, 0.8f), Offset(0.7f, 0.6f), Offset(1.0f, 0.7f),
        Offset(0.0f, 1.0f), Offset(0.3f, 1.0f), Offset(0.7f, 1.0f), Offset(1.0f, 1.0f)
    )
}

val gradientPainter = remember {
    MeshGradientPainter(rows = 3, columns = 3) {
        // Row 0
        setVertex(0, 0, points[0], yellow)
        setVertex(0, 1, points[1], orange)
        setVertex(0, 2, points[2], yellow)
        setVertex(0, 3, points[3], purple)

        // Row 1
        setVertex(1, 0, points[4], pink)
        setVertex(1, 1, points[5], yellow)
        setVertex(1, 2, points[6], pink)
        setVertex(1, 3, points[7], purple)

        // Row 2
        setVertex(2, 0, points[8], indigo)
        setVertex(2, 1, points[9], pink)
        setVertex(2, 2, points[10], purple)
        setVertex(2, 3, points[11], indigo)

        // Row 3
        setVertex(3, 0, points[12], purple)
        setVertex(3, 1, points[13], indigo)
        setVertex(3, 2, points[14], pink)
        setVertex(3, 3, points[15], yellow)
    }
}

Box(
    modifier = modifier.padding(32.dp)
        .aspectRatio(16 / 9f)
        .fillMaxWidth()
        .paint(gradientPainter)
        // ...
)

تدرّج شبكي مع نقاط تحكّم في منحنى بيزييه وألوان متموّجة، وتظهر نقاط الشبكة فوقه.
الشكل 4 تدرّج شبكي مع نقاط تحكّم في منحنى بيزييه وألوان متموّجة، وتظهر نقاط الشبكة فوقه.

إضافة مؤثرات حركية إلى تدرّج شبكي

بما أنّ مَعلمة lambda‏ block في MeshGradientPainter يتم تنفيذها ضمن DrawScope، يمكنها قراءة الحالة القابلة للتغيير ومراقبتها. يمكنك تحريك المواضع أو الألوان بمرور الوقت بدون إعادة تخصيص أدوات التظليل أو الصور النقطية.

val infiniteTransition = rememberInfiniteTransition(label = "meshMovement")
val animatedOffset by infiniteTransition.animateFloat(
    initialValue = -0.1f,
    targetValue = 0.1f,
    animationSpec = infiniteRepeatable(
        animation = tween(2500, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    ),
    label = "offset"
)

val coral = Color(255, 90, 90)
val peach = Color(255, 139, 90)
val amber = Color(255, 169, 90)
val sunshine = Color(255, 212, 90)
val indigo = Color(0xFF5856D6)
val pink = Color(0xFFFF2D55)


val gradientPainter = remember {
    MeshGradientPainter(rows = 3, columns = 3) {
        // Row 0
        setVertex(0, 0, Offset(0.0f, 0.0f), indigo)
        setVertex(0, 1, Offset(0.3f, 0.0f), peach)
        setVertex(0, 2, Offset(0.7f, 0.0f), amber)
        setVertex(0, 3, Offset(1.0f, 0.0f), sunshine)
        // Row 1
        setVertex(1, 0, Offset(0.0f, 0.3f), pink)
        setVertex(1, 1, Offset(0.2f, 0.4f) + Offset(animatedOffset, animatedOffset), coral)
        setVertex(1, 2, Offset(0.7f, 0.2f) + Offset(animatedOffset, animatedOffset), peach)
        setVertex(1, 3, Offset(1.0f, 0.3f), indigo)

        // Row 2
        setVertex(2, 0, Offset(0.0f, 0.7f), coral)
        setVertex(2, 1, Offset(0.3f, 0.8f) + Offset(animatedOffset, 0f), pink)
        setVertex(2, 2, Offset(0.7f, 0.6f) + Offset(animatedOffset, 0f), sunshine)
        setVertex(2, 3, Offset(1.0f, 0.7f), amber)

        // Row 3
        setVertex(3, 0, Offset(0.0f, 1.0f), sunshine)
        setVertex(3, 1, Offset(0.3f, 1.0f), amber)
        setVertex(3, 2, Offset(0.7f, 1.0f), pink)
        setVertex(3, 3, Offset(1.0f, 1.0f), indigo)
    }
}


Box(
    modifier = modifier.padding(32.dp)
        .safeContentPadding()
        .aspectRatio(16 / 9f)
        .fillMaxWidth()
        .paint(gradientPainter)
)

الشكل 5. تدرّج شبكي متحرّك مع نقاط لعرض الحركة