سفارشی‌سازی پویانمایی‌ها

بسیاری از Animation API معمولاً پارامترهایی را برای سفارشی‌سازی رفتارشان می‌پذیرند.

سفارشی‌سازی پویانمایی‌ها با پارامتر AnimationSpec

اکثر میاناهای برنامه‌سازی کاربردی پویانمایی به توسعه‌دهندگان اجازه می‌دهند مشخصات پویانمایی را با پارامتر اختیاری AnimationSpec سفارشی‌سازی کنند.

val alpha: Float by animateFloatAsState(
    targetValue = if (enabled) 1f else 0.5f,
    // Configure the animation duration and easing.
    animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing),
    label = "alpha"
)

انواع مختلفی از AnimationSpec برای ایجاد انواع مختلف پویانمایی وجود دارد.

ایجاد پویانمایی مبتنی بر فیزیک با spring

‫spring پویانمایی مبتنی بر فیزیک بین مقادیر شروع و پایان ایجاد می‌کند. این تابع ۲ پارامتر می‌گیرد: dampingRatio و stiffness.

dampingRatio مشخص می‌کند که فنر چقدر باید جهنده باشد. مقدار پیش‌فرض Spring.DampingRatioNoBouncy است.

شکل ۱. تنظیم نسبت‌های مختلف میرایی فنر.

stiffness تعیین می‌کند که فنر با چه سرعتی باید به‌سمت مقدار نهایی حرکت کند. مقدار پیش‌فرض Spring.StiffnessMedium است.

شکل ۲. تنظیم سختی‌های مختلف فنر.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioHighBouncy,
        stiffness = Spring.StiffnessMedium
    ),
    label = "spring spec"
)

spring می‌تواند وقفه‌ها را روان‌تر از انواع مبتنی بر مدت‌زمان AnimationSpec مدیریت کند، زیرا تداوم سرعت را هنگام تغییر مقدار هدف در میان پویانمایی‌ها تضمین می‌کند. ‫spring به‌عنوان AnimationSpec پیش‌فرض توسط بسیاری از میاناهای برنامه‌سازی کاربردی پویانمایی، مانند animate*AsState و updateTransition، استفاده می‌شود.

برای مثال، اگر پیکربندی spring را روی پویانمایی زیر که با لمس کاربر هدایت می‌شود اعمال کنیم، هنگام قطع کردن پویانمایی درحین پیشرفت آن، می‌توانید ببینید که استفاده از tween به نرمی استفاده از spring پاسخ نمی‌دهد.

شکل ۳. تنظیم کردن مشخصات tween درمقابل spring برای پویانمایی و قطع کردن آن.

پویانمایی بین مقادیر شروع و پایان با منحنی هموارسازی با tween

‫tween بین مقادیر شروع و پایان در طول مدت مشخص‌شده durationMillis بااستفاده از منحنی هموارسازی پویانمایی می‌کند. tween مخفف کلمه «بین» است - چون بین دو مقدار قرار می‌گیرد.

همچنین می‌توانید delayMillis را برای به‌تعویق انداختن شروع پویانمایی مشخص کنید.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = tween(
        durationMillis = 300,
        delayMillis = 50,
        easing = LinearOutSlowInEasing
    ),
    label = "tween delay"
)

برای اطلاعات بیشتر، به آسان‌سازی مراجعه کنید.

با keyframes، در زمان‌بندی‌های خاص به مقادیر مشخصی پویانمایی کنید

keyframes براساس مقادیر عکس آنی مشخص‌شده در مُهرهای زمان مختلف در طول پویانمایی، پویانمایی می‌کند. در هر زمان مشخص، مقدار پویانمایی بین دو مقدار فریم کلیدی درون‌یابی خواهد شد. برای هرکدام از این قاب‌های کلیدی، می‌توان «تسهیل» را برای تعیین منحنی درون‌یابی مشخص کرد.

مشخص کردن مقادیر در ۰ میلی‌ثانیه و در زمان مدت اختیاری است. اگر این مقادیر را مشخص نکنید، به‌طور پیش‌فرض به‌ترتیب روی مقادیر شروع و پایان پویانمایی تنظیم می‌شوند.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = keyframes {
        durationMillis = 375
        0.0f at 0 using LinearOutSlowInEasing // for 0-15 ms
        0.2f at 15 using FastOutLinearInEasing // for 15-75 ms
        0.4f at 75 // ms
        0.4f at 225 // ms
    },
    label = "keyframe"
)

با keyframesWithSplines، پویانمایی بین قاب‌های کلیدی را به‌آرامی انجام دهید

برای ایجاد پویانمایی‌ای که هنگام گذر بین مقادیر از منحنی همواری پیروی می‌کند، می‌توانید به‌جای مشخصات پویانمایی keyframes از keyframesWithSplines استفاده کنید.

val offset by animateOffsetAsState(
    targetValue = Offset(300f, 300f),
    animationSpec = keyframesWithSpline {
        durationMillis = 6000
        Offset(0f, 0f) at 0
        Offset(150f, 200f) atFraction 0.5f
        Offset(0f, 100f) atFraction 0.7f
    }
)

قاب‌های کلیدی مبتنی بر اسپلاین به‌ویژه برای حرکت دوبعدی موارد روی صفحه‌نمایش مفید هستند.

ویدیوهای زیر تفاوت‌های بین keyframes و keyframesWithSpline را با درنظر گرفتن مجموعه یکسانی از مختصات x و y که دایره باید دنبال کند نشان می‌دهند.

keyframes keyframesWithSplines

همان‌طور که می‌بینید، قاب‌های کلیدی مبتنی بر اسپلاین گذارهای هموارتری بین نقاط ارائه می‌دهند، زیرا از منحنی‌های بزیه برای پویانمایی هموار بین موارد استفاده می‌کنند. این مشخصه برای پویانمایی ازپیش‌تنظیم‌شده مفید است. بااین‌حال، اگر با نقاطی کار می‌کنید که توسط کاربر هدایت می‌شوند، بهتر است از فنرها برای دستیابی به نرمی مشابه بین نقاط استفاده کنید، زیرا این نقاط قابل‌قطع هستند.

تکرار پویانمایی با repeatable

repeatable پویانمایی مبتنی بر مدت (مانند tween یا keyframes) را به‌طور مکرر اجرا می‌کند تا به تعداد تکرار مشخص‌شده برسد. می‌توانید پارامتر repeatMode را ارسال کنید تا مشخص کنید که پویانمایی باید با شروع از ابتدا (RepeatMode.Restart) یا از انتها (RepeatMode.Reverse) تکرار شود.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = repeatable(
        iterations = 3,
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    ),
    label = "repeatable spec"
)

تکرار بی‌نهایت پویانمایی با infiniteRepeatable

infiniteRepeatable مانند repeatable است، اما برای تعداد بی‌نهایتی از تکرارها تکرار می‌شود.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = infiniteRepeatable(
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    ),
    label = "infinite repeatable"
)

در آزمایش‌هایی که از ComposeTestRule استفاده می‌کنند، پویانمایی‌هایی که از infiniteRepeatable استفاده می‌کنند اجرا نمی‌شوند. مؤلفه بااستفاده از مقدار اولیه هر مقدار پویانمایی‌شده ارائه خواهد شد.

پرش فوری به مقدار نهایی با snap

‫snap یک AnimationSpec ویژه است که بلافاصله مقدار را به مقدار نهایی تغییر می‌دهد. می‌توانید delayMillis را برای به‌تأخیر انداختن شروع پویانمایی مشخص کنید.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = snap(delayMillis = 50),
    label = "snap spec"
)

تنظیم تابع هموارسازی سفارشی

عملیات‌های AnimationSpec مبتنی بر مدت (مانند tween یا keyframes) از Easing برای تنظیم کسر پویانمایی استفاده می‌کنند. این کار به مقدار پویانمایی اجازه می‌دهد به‌جای حرکت با نرخ ثابت، سرعتش را افزایش و کاهش دهد. کسر مقداری بین ۰ (شروع) و ۱٫۰ (پایان) است که نقطه فعلی را در پویانمایی نشان می‌دهد.

آرامش درواقع تابعی است که مقدار کسری بین ۰ و ۱٫۰ را می‌گیرد و مقدار اعشاری برمی‌گرداند. مقدار برگردانده‌شده می‌تواند خارج از مرز باشد تا فراتر رفتن یا کمتر بودن را نشان دهد. می‌توانید یک «آرامش» سفارشی مانند کد زیر ایجاد کنید.

val CustomEasing = Easing { fraction -> fraction * fraction }

@Composable
fun EasingUsage() {
    val value by animateFloatAsState(
        targetValue = 1f,
        animationSpec = tween(
            durationMillis = 300,
            easing = CustomEasing
        ),
        label = "custom easing"
    )
    // ……
}

Compose چندین تابع Easing داخلی ارائه می‌دهد که اکثر موارد استفاده را پوشش می‌دهند. برای اطلاعات بیشتر درباره اینکه بسته به سناریو شما از کدام «آرام‌سازی» استفاده شود، به سرعت - طراحی مواد مراجعه کنید.

پویانمایی کردن انواع داده‌های سفارشی با تبدیل به و از AnimationVector

اکثر میاناهای برنامه‌سازی کاربردی پویانمایی Compose به‌طور پیش‌فرض از Float، Color، Dp، و دیگر انواع داده‌های پایه به‌عنوان مقادیر پویانمایی پشتیبانی می‌کنند، اما گاهی لازم است انواع داده‌های دیگر، ازجمله انواع سفارشی خودتان، را پویانمایی کنید. درطول پویانمایی، هر مقدار پویانمایی به‌صورت AnimationVector نشان داده می‌شود. مقدار به AnimationVector و برعکس توسط TwoWayConverter مربوطه تبدیل می‌شود تا سیستم پویانمایی اصلی بتواند آن‌ها را به‌طور یکنواخت مدیریت کند. برای مثال، Int به‌صورت AnimationVector1D نمایش داده می‌شود که یک مقدار ممیز شناور را نگه می‌دارد. TwoWayConverter برای Int این‌گونه است:

val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
    TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })

Color اساساً مجموعه‌ای از ۴ مقدار قرمز، سبز، آبی، و آلفا است، بنابراین Color به AnimationVector4D تبدیل می‌شود که ۴ مقدار ممیز شناور را نگه می‌دارد. به این ترتیب، هر نوع داده‌ای که در پویانمایی‌ها استفاده می‌شود، بسته به ابعاد آن، به AnimationVector1D، AnimationVector2D، AnimationVector3D، یا AnimationVector4D تبدیل می‌شود. این کار به مؤلفه‌های مختلف شیء اجازه می‌دهد به‌طور مستقل پویانمایی شوند و هرکدام ردیابی سرعت خود را داشته باشند. بااستفاده از مبدل‌هایی مثل Color.VectorConverter یا Dp.VectorConverter می‌توان به مبدل‌های داخلی برای انواع داده‌های پایه دسترسی پیدا کرد.

وقتی می‌خواهید پشتیبانی از نوع داده جدیدی را به‌عنوان مقدار پویانمایی اضافه کنید، می‌توانید TwoWayConverter خودتان را ایجاد کنید و آن را به میانای برنامه‌سازی کاربردی ارائه دهید. برای مثال، می‌توانید از animateValueAsState برای پویانمایی نوع داده سفارشی‌تان به این صورت استفاده کنید:

data class MySize(val width: Dp, val height: Dp)

@Composable
fun MyAnimation(targetSize: MySize) {
    val animSize: MySize by animateValueAsState(
        targetSize,
        TwoWayConverter(
            convertToVector = { size: MySize ->
                // Extract a float value from each of the `Dp` fields.
                AnimationVector2D(size.width.value, size.height.value)
            },
            convertFromVector = { vector: AnimationVector2D ->
                MySize(vector.v1.dp, vector.v2.dp)
            }
        ),
        label = "size"
    )
}

فهرست زیر شامل برخی‌از VectorConverterهای داخلی است: