سفارشی کردن انیمیشن ها

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

انیمیشن‌ها را با پارامتر AnimationSpec سفارشی کنید

بیشتر APIهای انیمیشن به توسعه‌دهندگان اجازه می‌دهند مشخصات انیمیشن را با استفاده از یک پارامتر اختیاری 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 پیش‌فرض توسط بسیاری از APIهای انیمیشن، مانند animate*AsState و updateTransition ، استفاده می‌شود.

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

شکل ۳. تنظیم مشخصات tween در مقابل spring برای انیمیشن و ایجاد وقفه در آن.

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

tween با استفاده از یک منحنی کاهشی، بین مقادیر شروع و پایان در durationMillis مشخص شده (میلی ثانیه) انیمیشن ایجاد می‌کند. tween مخفف کلمه between است - زیرا بین دو مقدار حرکت می‌کند.

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

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

برای اطلاعات بیشتر به Easing مراجعه کنید.

با استفاده keyframes در زمان‌بندی‌های مشخص، به مقادیر خاص انیمیشن دهید

keyframes بر اساس مقادیر snapshot مشخص شده در timestamps های مختلف در طول مدت انیمیشن، انیمیشن ایجاد می‌کنند. در هر زمان معین، مقدار انیمیشن بین دو مقدار فریم کلیدی درون‌یابی می‌شود. برای هر یک از این فریم‌های کلیدی، می‌توان Easing را برای تعیین منحنی درون‌یابی مشخص کرد.

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

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

همانطور که می‌بینید، فریم‌های کلیدی مبتنی بر spline، انتقال‌های نرم‌تری بین نقاط ارائه می‌دهند، زیرا از منحنی‌های bezier برای متحرک‌سازی روان بین آیتم‌ها استفاده می‌کنند. این مشخصات برای یک انیمیشن از پیش تعیین‌شده مفید است. با این حال، اگر با نقاطی که توسط کاربر هدایت می‌شوند کار می‌کنید، ترجیحاً از springs برای دستیابی به نرمی مشابه بین نقاط استفاده کنید زیرا آنها قابل قطع شدن هستند.

تکرار یک انیمیشن با 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 برای تنظیم کسر انیمیشن استفاده می‌کنند. این به مقدار انیمیشن اجازه می‌دهد تا سرعت بگیرد و کند شود، نه اینکه با سرعت ثابت حرکت کند. کسر مقداری بین 0 (شروع) و 1.0 (پایان) است که نقطه فعلی انیمیشن را نشان می‌دهد.

در واقع Easing تابعی است که یک مقدار کسری بین ۰ تا ۱.۰ می‌گیرد و یک عدد اعشاری برمی‌گرداند. مقدار برگشتی می‌تواند خارج از مرز باشد تا نشان‌دهنده‌ی overshoot یا undershoot باشد. می‌توان یک 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 داخلی ارائه می‌دهد که اکثر موارد استفاده را پوشش می‌دهد. برای اطلاعات بیشتر در مورد اینکه بسته به سناریوی شما از چه Easing استفاده کنید، به Speed ​​- Material Design مراجعه کنید.

  • FastOutSlowInEasing
  • LinearOutSlowInEasing
  • FastOutLinearEasing
  • LinearEasing
  • CubicBezierEasing
  • بیشتر ببینید

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

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

{% کلمه به کلمه %} {% فعل کمکی %} {% کلمه به کلمه %} {% فعل کمکی %}