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

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

پویانمایی‌ها را با پارامتر 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"
    )
    // ……
}

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

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

اکثر «میاناهای برنامه‌سازی کاربردی» پویانمایی Compose به‌طور پیش‌فرض از Float، Color، Dp، و انواع داده‌های پایه دیگر به‌عنوان مقادیر پویانمایی پشتیبانی می‌کنند، اما گاهی اوقات باید انواع داده‌های دیگر ازجمله انواع داده‌های سفارشی خودتان را پویانمایی کنید. درطول پویانمایی، هر مقدار پویانمایی به‌صورت AnimationVector نشان داده می‌شود. این مقدار توسط TwoWayConverter مربوطه به AnimationVector تبدیل می‌شود و بالعکس، تا سیستم اصلی پویانمایی بتواند آن‌ها را به‌صورت یکنواخت مدیریت کند. برای مثال، 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 خودتان را ایجاد کنید و آن را به API ارائه دهید. برای مثال، می‌توانید از 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های داخلی است: