تنشئ التدرجات الشبكية انتقالات ألوان معقّدة ومتعددة الاتجاهات باستخدام شبكة ثنائية الأبعاد من الرقع. على عكس التدرجات الخطية أو الدائرية، تعمل تدرجات الشبكة على استيفاء الألوان بسلاسة عبر شبكة. استخدِم تدرّجات شبكية لإنشاء عناصر جمالية سلسة وطبيعية في واجهة المستخدم.
المفاهيم الرئيسية
لإنشاء تدرّج شبكي، حدِّد أبعاد الشبكة والرؤوس وانتقالات الألوان بين النقاط:
- أبعاد الشبكة: يتم تقسيم الشبكة إلى رقع على طول المحورين العمودي والأفقي. تحتوي شبكة من
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) )
استخدام نقاط تحكّم 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) // ... )
تحريك تدرّج شبكي
بما أنّ مَعلمة block في دالة lambda الخاصة بـ 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) )