মান-ভিত্তিক অ্যানিমেশন

এই পৃষ্ঠায় জেটপ্যাক কম্পোজে কীভাবে ভ্যালু-ভিত্তিক অ্যানিমেশন তৈরি করতে হয় তা বর্ণনা করা হয়েছে, বিশেষ করে সেইসব এপিআই-এর উপর আলোকপাত করা হয়েছে যেগুলো ভ্যালুর বর্তমান এবং টার্গেট স্টেটের উপর ভিত্তি করে সেগুলোকে অ্যানিমেট করে।

animate*AsState ব্যবহার করে একটিমাত্র মানকে অ্যানিমেট করুন।

Compose-এ animate*AsState ফাংশনগুলো হলো একটিমাত্র ভ্যালুকে অ্যানিমেট করার জন্য ব্যবহৃত সহজবোধ্য অ্যানিমেশন এপিআই। আপনাকে শুধু টার্গেট ভ্যালু (বা শেষ ভ্যালু) প্রদান করতে হয় এবং এপিআইটি বর্তমান ভ্যালু থেকে নির্দিষ্ট ভ্যালু পর্যন্ত অ্যানিমেশন শুরু করে।

নিম্নলিখিত উদাহরণটি এই API ব্যবহার করে আলফাকে অ্যানিমেট করে। টার্গেট ভ্যালুটিকে animateFloatAsState এর মধ্যে র‍্যাপ করার মাধ্যমে, আলফা ভ্যালুটি এখন প্রদত্ত মানগুলির (এই ক্ষেত্রে 1f বা 0.5f ) মধ্যে একটি অ্যানিমেশন ভ্যালু হয়ে যায়।

var enabled by remember { mutableStateOf(true) }

val animatedAlpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f, label = "alpha")
Box(
    Modifier
        .fillMaxSize()
        .graphicsLayer { alpha = animatedAlpha }
        .background(Color.Red)
)

আপনার কোনো অ্যানিমেশন ক্লাসের ইনস্ট্যান্স তৈরি করার বা ইন্টারাপশন হ্যান্ডেল করার প্রয়োজন নেই। আড়ালে, কল করার স্থানেই একটি অ্যানিমেশন অবজেক্ট (বিশেষত, একটি Animatable ইনস্ট্যান্স) তৈরি হয়ে যায় এবং প্রথম টার্গেট ভ্যালুটিকে তার প্রাথমিক মান হিসেবে মনে রাখা হয়। এরপর থেকে, যখনই আপনি এই কম্পোজেবলটিকে একটি ভিন্ন টার্গেট ভ্যালু দেবেন, সেই ভ্যালুর দিকে একটি অ্যানিমেশন স্বয়ংক্রিয়ভাবে শুরু হয়ে যাবে। যদি আগে থেকেই কোনো অ্যানিমেশন চালু থাকে, তবে অ্যানিমেশনটি তার বর্তমান ভ্যালু (এবং ভেলোসিটি) থেকে শুরু হয়ে টার্গেট ভ্যালুর দিকে অগ্রসর হয়। অ্যানিমেশন চলাকালীন, এই কম্পোজেবলটি প্রতি ফ্রেমে পুনরায় গঠিত হয় এবং একটি আপডেট করা অ্যানিমেশন ভ্যালু রিটার্ন করে।

ডিফল্টরূপে, Compose, Float , Color , Dp , Size , Offset , Rect , Int , IntOffset , এবং IntSize এর জন্য animate*AsState ফাংশন সরবরাহ করে। আপনি animateValueAsState এ একটি TwoWayConverter প্রদান করে অন্যান্য ডেটা টাইপের জন্য সমর্থন যোগ করতে পারেন, যা একটি জেনেরিক টাইপ গ্রহণ করে।

আপনি একটি AnimationSpec প্রদান করে অ্যানিমেশনের স্পেসিফিকেশনগুলো কাস্টমাইজ করতে পারেন। আরও তথ্যের জন্য AnimationSpec দেখুন।

একটি ট্রানজিশনের মাধ্যমে একই সাথে একাধিক প্রপার্টি অ্যানিমেট করুন

Transition এক বা একাধিক অ্যানিমেশনকে তার চাইল্ড হিসেবে পরিচালনা করে এবং সেগুলোকে একাধিক স্টেটের মধ্যে একযোগে চালায়।

স্টেটগুলো যেকোনো ডেটা টাইপের হতে পারে। অনেক ক্ষেত্রে, টাইপ সেফটি যাচাই করার জন্য আপনি একটি কাস্টম enum টাইপ ব্যবহার করতে পারেন, যেমনটি এই উদাহরণে দেখানো হয়েছে:

enum class BoxState {
    Collapsed,
    Expanded
}

updateTransition Transition এর একটি ইনস্ট্যান্স তৈরি করে ও মনে রাখে এবং এর অবস্থা হালনাগাদ করে।

var currentState by remember { mutableStateOf(BoxState.Collapsed) }
val transition = updateTransition(currentState, label = "box state")

এরপর আপনি এই ট্রানজিশনে একটি চাইল্ড অ্যানিমেশন সংজ্ঞায়িত করতে animate* এক্সটেনশন ফাংশনগুলোর একটি ব্যবহার করতে পারেন। প্রতিটি স্টেটের জন্য টার্গেট ভ্যালু নির্দিষ্ট করে দিন। এই animate* ফাংশনগুলো একটি অ্যানিমেশন ভ্যালু রিটার্ন করে, যা updateTransition দিয়ে ট্রানজিশন স্টেট আপডেট করা হলে অ্যানিমেশন চলাকালীন প্রতি ফ্রেমে আপডেট হয়।

val rect by transition.animateRect(label = "rectangle") { state ->
    when (state) {
        BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f)
        BoxState.Expanded -> Rect(100f, 100f, 300f, 300f)
    }
}
val borderWidth by transition.animateDp(label = "border width") { state ->
    when (state) {
        BoxState.Collapsed -> 1.dp
        BoxState.Expanded -> 0.dp
    }
}

ঐচ্ছিকভাবে, আপনি ট্রানজিশন স্টেট পরিবর্তনের প্রতিটি সমন্বয়ের জন্য একটি ভিন্ন AnimationSpec নির্দিষ্ট করতে একটি transitionSpec প্যারামিটার পাস করতে পারেন। আরও তথ্যের জন্য AnimationSpec দেখুন।

val color by transition.animateColor(
    transitionSpec = {
        when {
            BoxState.Expanded isTransitioningTo BoxState.Collapsed ->
                spring(stiffness = 50f)

            else ->
                tween(durationMillis = 500)
        }
    }, label = "color"
) { state ->
    when (state) {
        BoxState.Collapsed -> MaterialTheme.colorScheme.primary
        BoxState.Expanded -> MaterialTheme.colorScheme.background
    }
}

একবার কোনো ট্রানজিশন তার নির্দিষ্ট স্টেটে পৌঁছে গেলে, Transition.currentState এবং Transition.targetState একই হয়ে যায়। ট্রানজিশনটি শেষ হয়েছে কিনা, তা বোঝার জন্য আপনি এটি একটি সংকেত হিসেবে ব্যবহার করতে পারেন।

কখনও কখনও, আপনি প্রথম টার্গেট স্টেট থেকে ভিন্ন একটি প্রাথমিক স্টেট রাখতে চাইতে পারেন। এটি করার জন্য আপনি MutableTransitionState এর সাথে updateTransition ব্যবহার করতে পারেন। উদাহরণস্বরূপ, এটি আপনাকে কোড কম্পোজিশনে প্রবেশ করার সাথে সাথেই অ্যানিমেশন শুরু করার সুযোগ দেয়।

// Start in collapsed state and immediately animate to expanded
var currentState = remember { MutableTransitionState(BoxState.Collapsed) }
currentState.targetState = BoxState.Expanded
val transition = rememberTransition(currentState, label = "box state")
// ……

একাধিক কম্পোজেবল ফাংশন জড়িত আরও জটিল ট্রানজিশনের জন্য, আপনি একটি চাইল্ড ট্রানজিশন তৈরি করতে createChildTransition ব্যবহার করতে পারেন। একটি জটিল কম্পোজেবলের একাধিক উপাদানের মধ্যে কাজগুলোকে আলাদা করার জন্য এই কৌশলটি কার্যকর। প্যারেন্ট ট্রানজিশনটি চাইল্ড ট্রানজিশনগুলোর সমস্ত অ্যানিমেশন ভ্যালু সম্পর্কে অবগত থাকে।

enum class DialerState { DialerMinimized, NumberPad }

@Composable
fun DialerButton(isVisibleTransition: Transition<Boolean>) {
    // `isVisibleTransition` spares the need for the content to know
    // about other DialerStates. Instead, the content can focus on
    // animating the state change between visible and not visible.
}

@Composable
fun NumberPad(isVisibleTransition: Transition<Boolean>) {
    // `isVisibleTransition` spares the need for the content to know
    // about other DialerStates. Instead, the content can focus on
    // animating the state change between visible and not visible.
}

@Composable
fun Dialer(dialerState: DialerState) {
    val transition = updateTransition(dialerState, label = "dialer state")
    Box {
        // Creates separate child transitions of Boolean type for NumberPad
        // and DialerButton for any content animation between visible and
        // not visible
        NumberPad(
            transition.createChildTransition {
                it == DialerState.NumberPad
            }
        )
        DialerButton(
            transition.createChildTransition {
                it == DialerState.DialerMinimized
            }
        )
    }
}

AnimatedVisibility এবং AnimatedContent সাথে ট্রানজিশন ব্যবহার করুন

AnimatedVisibility এবং AnimatedContent Transition এর এক্সটেনশন ফাংশন হিসেবে উপলব্ধ। Transition.AnimatedVisibility এবং Transition.AnimatedContent এর targetState , Transition থেকে নির্ধারিত হয় এবং Transition এর targetState পরিবর্তিত হলে প্রয়োজন অনুযায়ী enter, exit, এবং sizeTransform অ্যানিমেশন ট্রিগার করে। এই এক্সটেনশন ফাংশনগুলো আপনাকে সেই সমস্ত enter, exit, এবং sizeTransform অ্যানিমেশনগুলোকে Transition মধ্যে নিয়ে আসতে দেয়, যা অন্যথায় AnimatedVisibility / AnimatedContent এর অভ্যন্তরীণ থাকত। এই এক্সটেনশন ফাংশনগুলোর সাহায্যে, আপনি বাইরে থেকে AnimatedVisibility / AnimatedContent এর অবস্থার পরিবর্তন পর্যবেক্ষণ করতে পারেন। একটি বুলিয়ান visible প্যারামিটারের পরিবর্তে, AnimatedVisibility এর এই সংস্করণটি একটি ল্যাম্বডা গ্রহণ করে, যা প্যারেন্ট ট্রানজিশনের টার্গেট স্টেটকে একটি বুলিয়ানে রূপান্তর করে।

বিস্তারিত জানতে AnimatedVisibility এবং AnimatedContent দেখুন।

var selected by remember { mutableStateOf(false) }
// Animates changes when `selected` is changed.
val transition = updateTransition(selected, label = "selected state")
val borderColor by transition.animateColor(label = "border color") { isSelected ->
    if (isSelected) Color.Magenta else Color.White
}
val elevation by transition.animateDp(label = "elevation") { isSelected ->
    if (isSelected) 10.dp else 2.dp
}
Surface(
    onClick = { selected = !selected },
    shape = RoundedCornerShape(8.dp),
    border = BorderStroke(2.dp, borderColor),
    shadowElevation = elevation
) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        Text(text = "Hello, world!")
        // AnimatedVisibility as a part of the transition.
        transition.AnimatedVisibility(
            visible = { targetSelected -> targetSelected },
            enter = expandVertically(),
            exit = shrinkVertically()
        ) {
            Text(text = "It is fine today.")
        }
        // AnimatedContent as a part of the transition.
        transition.AnimatedContent { targetState ->
            if (targetState) {
                Text(text = "Selected")
            } else {
                Icon(imageVector = Icons.Default.Phone, contentDescription = "Phone")
            }
        }
    }
}

একটি ট্রানজিশনকে এনক্যাপসুলেট করুন এবং এটিকে পুনঃব্যবহারযোগ্য করে তুলুন।

সহজ ব্যবহারের ক্ষেত্রে, আপনার UI-এর সাথে একই কম্পোজেবলে ট্রানজিশন অ্যানিমেশন সংজ্ঞায়িত করা একটি বৈধ বিকল্প। তবে, একাধিক অ্যানিমেটেড ভ্যালু সহ একটি জটিল কম্পোনেন্টে কাজ করার সময়, আপনি কম্পোজেবল UI থেকে অ্যানিমেশন ইমপ্লিমেন্টেশনকে আলাদা করতে চাইতে পারেন।

আপনি এমন একটি ক্লাস তৈরি করে এটি করতে পারেন, যা সমস্ত অ্যানিমেশন ভ্যালু ধারণ করবে এবং একটি update ফাংশন তৈরি করবেন যা সেই ক্লাসের একটি ইনস্ট্যান্স রিটার্ন করবে। আপনি ট্রানজিশন ইমপ্লিমেন্টেশনটিকে নতুন আলাদা ফাংশনের মধ্যে নিয়ে আসতে পারেন। এই প্যাটার্নটি তখন উপযোগী হয় যখন আপনাকে অ্যানিমেশন লজিককে কেন্দ্রীভূত করতে হয় অথবা জটিল অ্যানিমেশনগুলোকে পুনঃব্যবহারযোগ্য করতে হয়।

enum class BoxState { Collapsed, Expanded }

@Composable
fun AnimatingBox(boxState: BoxState) {
    val transitionData = updateTransitionData(boxState)
    // UI tree
    Box(
        modifier = Modifier
            .background(transitionData.color)
            .size(transitionData.size)
    )
}

// Holds the animation values.
private class TransitionData(
    color: State<Color>,
    size: State<Dp>
) {
    val color by color
    val size by size
}

// Create a Transition and return its animation values.
@Composable
private fun updateTransitionData(boxState: BoxState): TransitionData {
    val transition = updateTransition(boxState, label = "box state")
    val color = transition.animateColor(label = "color") { state ->
        when (state) {
            BoxState.Collapsed -> Color.Gray
            BoxState.Expanded -> Color.Red
        }
    }
    val size = transition.animateDp(label = "size") { state ->
        when (state) {
            BoxState.Collapsed -> 64.dp
            BoxState.Expanded -> 128.dp
        }
    }
    return remember(transition) { TransitionData(color, size) }
}

rememberInfiniteTransition ব্যবহার করে একটি অসীমভাবে পুনরাবৃত্তিমূলক অ্যানিমেশন তৈরি করুন।

InfiniteTransition , Transition মতোই এক বা একাধিক চাইল্ড অ্যানিমেশন ধারণ করে, কিন্তু এই অ্যানিমেশনগুলো কম্পোজিশনে প্রবেশ করার সাথে সাথেই চলতে শুরু করে এবং অপসারণ না করা পর্যন্ত থামে না। আপনি rememberInfiniteTransition ব্যবহার করে InfiniteTransition এর একটি ইনস্ট্যান্স তৈরি করতে পারেন এবং animateColor , animatedFloat বা animatedValue ব্যবহার করে চাইল্ড অ্যানিমেশন যোগ করতে পারেন। অ্যানিমেশনের স্পেসিফিকেশন নির্দিষ্ট করার জন্য আপনাকে একটি infiniteRepeatable ও উল্লেখ করতে হবে।

val infiniteTransition = rememberInfiniteTransition(label = "infinite")
val color by infiniteTransition.animateColor(
    initialValue = Color.Red,
    targetValue = Color.Green,
    animationSpec = infiniteRepeatable(
        animation = tween(1000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    ),
    label = "color"
)

Box(
    Modifier
        .fillMaxSize()
        .background(color)
)

নিম্ন-স্তরের অ্যানিমেশন এপিআই

পূর্ববর্তী বিভাগে উল্লিখিত সমস্ত উচ্চ-স্তরের অ্যানিমেশন এপিআই নিম্ন-স্তরের অ্যানিমেশন এপিআইগুলোর উপর ভিত্তি করে নির্মিত।

animate*AsState ফাংশনগুলো হলো সহজবোধ্য এপিআই, যা একটি তাৎক্ষণিক মানের পরিবর্তনকে অ্যানিমেশন মান হিসেবে রেন্ডার করে। এই কার্যকারিতাটি Animatable দ্বারা সমর্থিত, যা একটি একক মানকে অ্যানিমেট করার জন্য একটি কো-রুটিন-ভিত্তিক এপিআই।

updateTransition একটি ট্রানজিশন অবজেক্ট তৈরি করে যা একাধিক অ্যানিমেটিং ভ্যালু পরিচালনা করতে পারে এবং স্টেট পরিবর্তনের সময় সেগুলোকে রান করতে পারে। rememberInfiniteTransition একই রকম, তবে এটি একটি ইনফিনিট ট্রানজিশন তৈরি করে যা একাধিক অ্যানিমেশন পরিচালনা করতে পারে এবং সেগুলো অনির্দিষ্টকালের জন্য চলতে থাকে। Animatable ছাড়া এই সবগুলো API-ই কম্পোজেবল, যার মানে হলো আপনি এই অ্যানিমেশনগুলো কম্পোজিশনের বাইরেও তৈরি করতে পারেন।

এই সমস্ত এপিআই আরও মৌলিক Animation এপিআই-এর উপর ভিত্তি করে তৈরি। যদিও বেশিরভাগ অ্যাপ সরাসরি Animation ব্যবহার করে না, আপনি উচ্চ-স্তরের এপিআই-এর মাধ্যমে এর কিছু কাস্টমাইজেশন ক্ষমতা ব্যবহার করতে পারেন। AnimationVector এবং AnimationSpec সম্পর্কে আরও তথ্যের জন্য ‘কাস্টমাইজ অ্যানিমেশন’ দেখুন।

নিম্ন-স্তরের অ্যানিমেশন এপিআইগুলির মধ্যে সম্পর্ক
চিত্র ১. নিম্ন-স্তরের অ্যানিমেশন এপিআইগুলোর মধ্যকার সম্পর্ক।

Animatable : কোরাউটিন-ভিত্তিক একক মানের অ্যানিমেশন

Animatable হলো একটি ভ্যালু হোল্ডার যা animateTo ব্যবহার করে ভ্যালু পরিবর্তনের সাথে সাথে সেটিকে অ্যানিমেট করতে পারে। এই এপিআই-টিই animate*AsState এর ইমপ্লিমেন্টেশনকে সমর্থন করে। এটি কনসিস্টেন্ট কন্টিনিউয়েশন এবং মিউচুয়াল এক্সক্লুসিভিটি নিশ্চিত করে, যার অর্থ হলো ভ্যালুর পরিবর্তন সর্বদা অবিচ্ছিন্ন থাকে এবং Compose চলমান যেকোনো অ্যানিমেশন বাতিল করে দেয়।

Animatable এর অনেক বৈশিষ্ট্য, যার মধ্যে animateTo অন্তর্ভুক্ত, হলো সাসপেন্ড ফাংশন। এর মানে হলো, আপনাকে অবশ্যই সেগুলোকে একটি উপযুক্ত কো-রুটিন স্কোপের মধ্যে রাখতে হবে। উদাহরণস্বরূপ, আপনি LaunchedEffect কম্পোজেবলটি ব্যবহার করে শুধুমাত্র নির্দিষ্ট কী ভ্যালুটির সময়কালের জন্য একটি স্কোপ তৈরি করতে পারেন।

// Start out gray and animate to green/red based on `ok`
val color = remember { Animatable(Color.Gray) }
LaunchedEffect(ok) {
    color.animateTo(if (ok) Color.Green else Color.Red)
}
Box(
    Modifier
        .fillMaxSize()
        .background(color.value)
)

পূর্ববর্তী উদাহরণে, আপনি Color.Gray এর প্রাথমিক মান দিয়ে Animatable এর একটি ইনস্ট্যান্স তৈরি ও মনে রাখেন। ok বুলিয়ান ফ্ল্যাগের মানের উপর নির্ভর করে, রঙটি হয় Color.Green অথবা Color.Red এ অ্যানিমেট হয়। বুলিয়ান মানের পরবর্তী যেকোনো পরিবর্তনে অন্য রঙে একটি অ্যানিমেশন শুরু হয়। মান পরিবর্তনের সময় যদি কোনো অ্যানিমেশন চলমান থাকে, তাহলে Compose সেই অ্যানিমেশনটি বাতিল করে দেয় এবং নতুন অ্যানিমেশনটি বর্তমান স্ন্যাপশট মান ও বর্তমান বেগ থেকে শুরু হয়।

এই Animatable API-টি হলো পূর্ববর্তী বিভাগে উল্লিখিত animate*AsState এর অন্তর্নিহিত বাস্তবায়ন। সরাসরি Animatable ব্যবহার করে বিভিন্ন উপায়ে আরও সূক্ষ্ম নিয়ন্ত্রণ লাভ করা যায়:

  • প্রথমত, Animatable একটি প্রাথমিক মান থাকতে পারে যা তার প্রথম লক্ষ্য মান থেকে ভিন্ন। উদাহরণস্বরূপ, পূর্ববর্তী কোড উদাহরণটিতে প্রথমে একটি ধূসর বাক্স দেখানো হয়েছে, যা অবিলম্বে অ্যানিমেট হয়ে সবুজ বা লাল রঙে রূপান্তরিত হয়।
  • দ্বিতীয়ত, Animatable কন্টেন্ট ভ্যালুর উপর আরও কিছু অপারেশন প্রদান করে, বিশেষত snapTo এবং animateDecay
    • snapTo বর্তমান মানকে তাৎক্ষণিকভাবে লক্ষ্য মানে সেট করে। এটি তখন উপযোগী হয় যখন অ্যানিমেশনই তথ্যের একমাত্র উৎস নয় এবং টাচ ইভেন্টের মতো অন্যান্য অবস্থার সাথে এটিকে সিঙ্ক্রোনাইজ করতে হয়।
    • animateDecay এমন একটি অ্যানিমেশন শুরু করে যা প্রদত্ত বেগ থেকে ধীরে ধীরে গতি কমিয়ে আনে। ফ্লিং বিহেভিয়ার বাস্তবায়নের জন্য এটি উপযোগী।

আরও তথ্যের জন্য জেসচার এবং অ্যানিমেশন দেখুন।

ডিফল্টরূপে, Animatable Float এবং Color সমর্থন করে, কিন্তু একটি TwoWayConverter প্রদান করে আপনি যেকোনো ডেটা টাইপ ব্যবহার করতে পারেন। আরও তথ্যের জন্য AnimationVector দেখুন।

আপনি একটি AnimationSpec প্রদান করে অ্যানিমেশনের স্পেসিফিকেশনগুলো কাস্টমাইজ করতে পারেন। আরও তথ্যের জন্য AnimationSpec দেখুন।

Animation : ম্যানুয়ালি নিয়ন্ত্রিত অ্যানিমেশন

Animation হলো উপলব্ধ সর্বনিম্ন স্তরের অ্যানিমেশন এপিআই। আমরা এখন পর্যন্ত যে অ্যানিমেশনগুলো দেখেছি, তার বেশিরভাগই Animation উপর ভিত্তি করে তৈরি। Animation দুটি উপপ্রকার রয়েছে: TargetBasedAnimation এবং DecayAnimation

শুধুমাত্র অ্যানিমেশনের সময় ম্যানুয়ালি নিয়ন্ত্রণ করতে Animation ব্যবহার করুন। Animation স্টেটলেস, এবং এর কোনো লাইফসাইকেলের ধারণা নেই। এটি উচ্চ-স্তরের এপিআই-এর জন্য একটি অ্যানিমেশন ক্যালকুলেশন ইঞ্জিন হিসেবে কাজ করে।

TargetBasedAnimation

অন্যান্য এপিআইগুলো বেশিরভাগ ব্যবহারের ক্ষেত্র পূরণ করে, কিন্তু সরাসরি TargetBasedAnimation ব্যবহার করে আপনি অ্যানিমেশনের প্লে টাইম নিয়ন্ত্রণ করতে পারেন। নিচের উদাহরণে, আপনি withFrameNanos দ্বারা প্রদত্ত ফ্রেম টাইমের উপর ভিত্তি করে TargetAnimation এর প্লে টাইম ম্যানুয়ালি নিয়ন্ত্রণ করছেন।

val anim = remember {
    TargetBasedAnimation(
        animationSpec = tween(200),
        typeConverter = Float.VectorConverter,
        initialValue = 200f,
        targetValue = 1000f
    )
}
var playTime by remember { mutableLongStateOf(0L) }

LaunchedEffect(anim) {
    val startTime = withFrameNanos { it }

    do {
        playTime = withFrameNanos { it } - startTime
        val animationValue = anim.getValueFromNanos(playTime)
    } while (someCustomCondition())
}

DecayAnimation

TargetBasedAnimation বিপরীতে, DecayAnimation জন্য কোনো targetValue প্রদান করার প্রয়োজন হয় না। এর পরিবর্তে, এটি initialVelocityinitialValue দ্বারা নির্ধারিত প্রারম্ভিক শর্তাবলী এবং সরবরাহকৃত DecayAnimationSpec এর উপর ভিত্তি করে নিজের targetValue গণনা করে।

ফ্লিং জেসচারের পরে এলিমেন্টগুলোকে ধীরে ধীরে থামানোর জন্য প্রায়শই ডিকে অ্যানিমেশন ব্যবহার করা হয়। অ্যানিমেশনের বেগ initialVelocityVector দ্বারা নির্ধারিত মান থেকে শুরু হয় এবং সময়ের সাথে সাথে কমে আসে।

{% হুবহু %} {% endverbatim %} {% হুবহু %} {% endverbatim %}