[Jetpack Compose] DecayAnimation

유민국·2024년 10월 18일

같이 보기
interface Animation
Animatable

DecayAnimation

DecayAnimation은 시간이 지남에 따라 초기 속도 벡터에서 점점 느려지는 애니메이션이다. 재생 시간이 주어졌을 때 값과 속도를 간편하게 조회할 수 있도록 지원하는 애니메이션 계산 엔진 역할을 한다.

DecayAnimation은 상태를 추적하거나 생명주기 관리가 필요하지 않은 stateless animation 이다.

주어진 시간에 따라 애니메이션의 값과 속도를 계산하는 역할을 하며, 내부적으로 시작 값(initialValue), 초기 속도(initialVelocityVector), 감속 애니메이션 스펙(decay animation spec), 타입 변환기(typeConverter) 등의 정보를 저장 하고 있다

타이밍을 수동으로 제어할 필요가 없는 경우, DecayAnimation을 기반으로 구축된 상위 레벨 애니메이션 API(ex: Animatable. animateDecay, AnimationState. animateDecay 등)를 사용하는 것을 권장한다

DecayAnimation 같은 경우 애니메이션이 특정 시점에서 자연스럽게 종료되도록 설계되어 있기 때문에 infinite = false 로 설정되어 있다

.
.
.
.
기본적으로 Animation 인터페이스를 override하여 감쇠 애니메이션을 만들기 때문에 아래의 코드를 확인하기 보다 Animation 인터페이스가 어떤 기능인지 확인하는게 좋을 것 같다.

코드

fun DecayAnimation(
    animationSpec: FloatDecayAnimationSpec,
    initialValue: Float,
    initialVelocity: Float = 0f
) = DecayAnimation(
    animationSpec.generateDecayAnimationSpec(),
    Float.VectorConverter,
    initialValue,
    AnimationVector(initialVelocity)
)

class DecayAnimation<T, V : AnimationVector> /*@VisibleForTesting*/ constructor(
    private val animationSpec: VectorizedDecayAnimationSpec<V>,
    override val typeConverter: TwoWayConverter<T, V>,
    val initialValue: T,
    initialVelocityVector: V
) : Animation<T, V> {
    private val initialValueVector: V = typeConverter.convertToVector(initialValue)
    val initialVelocityVector: V = initialVelocityVector.copy()
    private val endVelocity: V

    override val targetValue: T = typeConverter.convertFromVector(
        animationSpec.getTargetValue(initialValueVector, initialVelocityVector)
    )
    @get:Suppress("MethodNameUnits")
    override val durationNanos: Long

    override val isInfinite: Boolean = false

    constructor(
        animationSpec: DecayAnimationSpec<T>,
        typeConverter: TwoWayConverter<T, V>,
        initialValue: T,
        initialVelocityVector: V
    ) : this(
        animationSpec.vectorize(typeConverter),
        typeConverter,
        initialValue,
        initialVelocityVector
    )
    
    constructor(
        animationSpec: DecayAnimationSpec<T>,
        typeConverter: TwoWayConverter<T, V>,
        initialValue: T,
        initialVelocity: T
    ) : this(
        animationSpec.vectorize(typeConverter),
        typeConverter,
        initialValue,
        typeConverter.convertToVector(initialVelocity)
    )

    init {
        durationNanos = animationSpec.getDurationNanos(
            initialValueVector, initialVelocityVector
        )
        endVelocity = animationSpec.getVelocityFromNanos(
            durationNanos,
            initialValueVector,
            initialVelocityVector
        ).copy()
        for (i in 0 until endVelocity.size) {
            endVelocity[i] = endVelocity[i].coerceIn(
                -animationSpec.absVelocityThreshold,
                animationSpec.absVelocityThreshold
            )
        }
    }

    override fun getValueFromNanos(playTimeNanos: Long): T {
        if (!isFinishedFromNanos(playTimeNanos)) {
            return typeConverter.convertFromVector(
                animationSpec.getValueFromNanos(
                    playTimeNanos,
                    initialValueVector,
                    initialVelocityVector
                )
            )
        } else {
            return targetValue
        }
    }

    override fun getVelocityVectorFromNanos(playTimeNanos: Long): V {
        if (!isFinishedFromNanos(playTimeNanos)) {
            return animationSpec.getVelocityFromNanos(
                playTimeNanos,
                initialValueVector,
                initialVelocityVector
            )
        } else {
            return endVelocity
        }
    }
}
profile
안녕하세요 😊

0개의 댓글