تُنشئ التدرّجات الشبكية انتقالات ألوان معقدة ومتعددة الاتجاهات باستخدام شبكة ثنائية الأبعاد من الرقع. على عكس التدرّجات الخطية أو الشعاعية، تُجري التدرّجات الشبكية استيفاءً سلسًا للألوان على مستوى الشبكة. استخدِم التدرّجات الشبكية لإنشاء عناصر جمالية سلسة وعضوية في واجهة المستخدم.
المفاهيم الرئيسية
لإنشاء تدرّج شبكي، حدِّد أبعاد الشبكة والرؤوس وانتقالات الألوان بين النقاط:
- أبعاد الشبكة: يتم تقسيم الشبكة إلى رقع على طول المحورَين العمودي والأفقي. تحتوي شبكة من
rowsوcolumnsعلى (صفوف+1)×(أعمدة+1) من الرؤوس. على سبيل المثال، تتكوّن الشبكة 1×1 من 4 رؤوس تشكّل رقعة واحدة. - الإحداثيات العادية: تستخدم جميع مواضع الرؤوس نظام إحداثيات عاديًا، حيث يمثّل
(0f, 0f)أعلى يسار حدود الرسم، ويمثّل(1f, 1f)أسفل يسارها. - نقاط تحكّم Bezier (المماسات): يحتوي كل رأس على ما يصل إلى أربع نقاط تحكّم اختيارية في Bezier. تحدّد هذه المماسات انحناء الحافة بين الرؤوس المجاورة. في حال استخدام
Offset.Unspecified، يستنتج Compose المماسات لضمان الانتقالات السلسة بين الرقع. تُنشئ كل خلية شبكة رقعة Bezier تتكوّن من 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) )
استخدام نقاط تحكّم Bezier معيّنة
تتولى أداة إنشاء الشبكة المتداخلة إجراء العمليات الحسابية المعقدة للحفاظ على سلاسة انتقالات الشبكة بشكلٍ تلقائي. ومع ذلك، يمكنك تخصيص المماسات بشكلٍ صريح على أي رأس واحد إذا أردت دفع أقسام ألوان معيّنة أو سحبها أو ضغطها بشكلٍ حاد.
يتم قياس إزاحات التحكّم بالنسبة إلى موضع رأس المضيف.
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، ما يعني أنّه يجب تحديد 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) // ... )
تحريك تدرّج شبكي
بما أنّ مَعلمة 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) )