گرادیان‌های توری

شیب‌های رنگی مش بااستفاده از شبکه دوبعدی از وصله‌ها، انتقال‌های رنگی پیچیده و چندجهتی ایجاد می‌کنند. برخلاف گرادیان‌های خطی یا شعاعی، گرادیان‌های توری به‌آرامی رنگ‌ها را در یک شبکه دوبعدی درون‌یابی می‌کنند. هنگام ایجاد پس‌زمینه‌های سیال، ارگانیک، یا چندنقطه‌ای در میانای کاربر، به‌جای چیدن چندین قلم‌موی radialGradient یا linearGradient روی هم، از گرادیان‌های مش استفاده کنید.

نمونه‌ای از گرادیان مش با نمایش نقاط گرادیان مش فعلی آن.
شکل ۱. نمونه‌ای از گرادیان مش با نمایش نقاط گرادیان مش فعلی آن.

مفاهیم اصلی

برای ساختن گرادیان مش، ابعاد شبکه، رئوس، و گذارهای رنگی بین نقاط را تعریف کنید:

  • ابعاد شبکه: مش در امتداد محورهای عمودی و افقی به وصله‌هایی تقسیم می‌شود. شبکه‌ای از rows و columns شامل (ردیف‌ها+۱)×(ستون‌ها+۱) رأس است. برای مثال، یک مش ۱×۱ از ۴ رأس تشکیل شده است که یک وصله را تشکیل می‌دهند.
  • مختصات نرمال‌شده: همه موقعیت‌های رأس از سیستم مختصات نرمال‌شده استفاده می‌کنند که در آن (0f, 0f) نشان‌دهنده بالا سمت چپ و (1f, 1f) نشان‌دهنده پایین سمت راست مرزهای طراحی است.
  • نقاط کنترل بزیه (مماس‌ها): هر رأس شامل حداکثر چهار نقطه کنترل بزیه اختیاری است. این مماس‌ها انحنای لبه بین رأس‌های همسایه را مشخص می‌کنند. اگر از Offset.Unspecified استفاده کنید، Compose مماس‌ها را استنباط می‌کند تا از انتقال روان در سراسر وصله‌ها اطمینان حاصل کند. هر سلول شبکه که از ۴ رأس به‌همراه نقاط کنترل آن‌ها تشکیل شده است، یک وصله بزیه تولید می‌کند.
  • درون‌یابی رنگ: چارچوب رنگ‌های بین رأس‌های اصلی را محاسبه می‌کند. برای انتقال رنگ ملایم‌تر، 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)
)

شیب توری ساده با ۴ رنگ تعریف‌شده در هر گوشه
شکل ۲. شیب رنگ مشبک ساده با چهار رنگ، که هر گوشه روی یکی از این چهار رنگ تنظیم شده است.

استفاده از نقاط کنترل بزیه خاص

به‌طور پیش‌فرض، تولیدکننده مش محاسبات پیچیده را انجام می‌دهد تا انتقال‌های شبکه هموار بماند. بااین‌حال، اگر می‌خواهید بخش‌های رنگی خاصی را به‌طور انتخابی فشار دهید، بکشید، یا به‌طور تیز نیشگون بگیرید، می‌توانید مماس‌ها را به‌طور صریح روی هر رأس تکی سفارشی‌سازی کنید.

انحراف‌های کنترل نسبت به موقعیت رأس میزبان اندازه‌گیری می‌شود.

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)
)

گرادیان مش با نقطه بالا سمت چپ منحنی.
شکل ۳. منحنی کردن رأس بالا سمت چپ با نقطه کنترل بزیه.

ایجاد شبکه‌های پیشرفته

این مثال یک شبکه ۳ در ۳ را نشان می‌دهد، یعنی ۱۶ نقطه وجود دارد که باید مشخص شود، و نقاط میانی با جابه‌جایی‌های مختلف تنظیم شده‌اند:

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 از 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)
)

شکل ۵. شیب مش پویانمایی‌شده با نقطه‌هایی برای نمایش پویانمایی.