![JAR search and dependency download from the Maven repository](/logo.png)
net.peanuuutz.fork.ui.animation.Animation.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fork-ui Show documentation
Show all versions of fork-ui Show documentation
Comprehensive API designed for Minecraft modders
The newest version!
/*
* Copyright 2020 The Android Open Source Project
* Modifications Copyright 2022 Peanuuutz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.peanuuutz.fork.ui.animation
import androidx.compose.runtime.withFrameMillis
import kotlinx.coroutines.CancellationException
import net.peanuuutz.fork.ui.animation.TimeConstants.UnspecifiedTimeMillis
import net.peanuuutz.fork.ui.animation.spec.decay.DefaultFloatDecayAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.decay.composite.DecayAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.decay.vectorized.VectorizedDecayAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.DefaultFloatAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.composite.AnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.vectorized.VectorizedAnimationSpec
import net.peanuuutz.fork.ui.animation.vector.AnimationVector
import net.peanuuutz.fork.ui.animation.vector.AnimationVector1D
import net.peanuuutz.fork.ui.animation.vector.VectorConvertor
import net.peanuuutz.fork.ui.animation.vector.copy
import net.peanuuutz.fork.ui.animation.vector.newVector
interface Animation> {
val isInfinite: Boolean
val convertor: VectorConvertor
val initialValue: S
val targetValue: S
val initialVelocityVector: V
val endVelocityVector: V
fun getValueFromMillis(playTimeMillis: Int): S
fun getVelocityVectorFromMillis(playTimeMillis: Int): V
val durationMillis: Int
}
fun > Animation.getValueVectorFromMillis(
playTimeMillis: Int
): V {
val value = getValueFromMillis(playTimeMillis)
return convertor.convertToVector(value)
}
fun > Animation.getVelocityFromMillis(
playTimeMillis: Int
): S {
val velocityVector = getVelocityVectorFromMillis(playTimeMillis)
return convertor.convertFromVector(velocityVector)
}
class TargetBasedAnimation>(
val vectorizedAnimationSpec: VectorizedAnimationSpec,
override val convertor: VectorConvertor,
override val initialValue: S,
override val targetValue: S,
initialVelocityVector: V? = null
) : Animation {
override val isInfinite: Boolean
get() = vectorizedAnimationSpec.isInfinite
private val initialValueVector: V = convertor.convertToVector(initialValue)
private val targetValueVector: V = convertor.convertToVector(targetValue)
override val initialVelocityVector: V = initialVelocityVector?.copy() ?: convertor.newVector(initialValue)
override val endVelocityVector: V = vectorizedAnimationSpec.getEndVelocity(
initialValue = initialValueVector,
targetValue = targetValueVector,
initialVelocity = this.initialVelocityVector
)
override fun getValueFromMillis(playTimeMillis: Int): S {
return if (playTimeMillis < durationMillis) {
convertor.convertFromVector(
vector = vectorizedAnimationSpec.getValueFromMillis(
playTimeMillis = playTimeMillis,
initialValue = initialValueVector,
targetValue = targetValueVector,
initialVelocity = initialVelocityVector
)
)
} else {
targetValue
}
}
override fun getVelocityVectorFromMillis(playTimeMillis: Int): V {
return if (playTimeMillis < durationMillis) {
vectorizedAnimationSpec.getVelocityFromMillis(
playTimeMillis = playTimeMillis,
initialValue = initialValueVector,
targetValue = targetValueVector,
initialVelocity = initialVelocityVector
)
} else {
endVelocityVector
}
}
override val durationMillis: Int = vectorizedAnimationSpec.getDurationMillis(
initialValue = initialValueVector,
targetValue = targetValueVector,
initialVelocity = this.initialVelocityVector
)
}
fun TargetBasedAnimation(
animationSpec: AnimationSpec = DefaultFloatAnimationSpec,
initialValue: Float,
targetValue: Float,
initialVelocity: Float = 0.0f
): TargetBasedAnimation {
return TargetBasedAnimation(
vectorizedAnimationSpec = animationSpec.vectorize(Float.VectorConvertor),
convertor = Float.VectorConvertor,
initialValue = initialValue,
targetValue = targetValue,
initialVelocityVector = Float.VectorConvertor.convertToVector(initialVelocity)
)
}
fun > AnimationSpec.createAnimation(
convertor: VectorConvertor,
initialValue: S,
targetValue: S,
initialVelocityVector: V? = null
): TargetBasedAnimation {
return TargetBasedAnimation(
vectorizedAnimationSpec = vectorize(convertor),
convertor = convertor,
initialValue = initialValue,
targetValue = targetValue,
initialVelocityVector = initialVelocityVector
)
}
class DecayAnimation>(
val vectorizedDecayAnimationSpec: VectorizedDecayAnimationSpec,
override val convertor: VectorConvertor,
override val initialValue: S,
initialVelocityVector: V? = null
) : Animation {
override val isInfinite: Boolean
get() = false
private val initialValueVector: V = convertor.convertToVector(initialValue)
override val initialVelocityVector: V = initialVelocityVector?.copy() ?: convertor.newVector(initialValue)
override val targetValue: S = convertor.convertFromVector(
vector = vectorizedDecayAnimationSpec.getTargetValue(initialValueVector, this.initialVelocityVector)
)
override val endVelocityVector: V = this.initialVelocityVector.new()
override fun getValueFromMillis(playTimeMillis: Int): S {
return if (playTimeMillis < durationMillis) {
convertor.convertFromVector(
vector = vectorizedDecayAnimationSpec.getValueFromMillis(
playTimeMillis = playTimeMillis,
initialValue = initialValueVector,
initialVelocity = initialVelocityVector
)
)
} else {
targetValue
}
}
override fun getVelocityVectorFromMillis(playTimeMillis: Int): V {
return if (playTimeMillis < durationMillis) {
vectorizedDecayAnimationSpec.getVelocityFromMillis(
playTimeMillis = playTimeMillis,
initialValue = initialValueVector,
initialVelocity = initialVelocityVector
)
} else {
endVelocityVector
}
}
override val durationMillis: Int = vectorizedDecayAnimationSpec.getDurationMillis(
initialValue = initialValueVector,
initialVelocity = this.initialVelocityVector
)
}
fun DecayAnimation(
decayAnimationSpec: DecayAnimationSpec = DefaultFloatDecayAnimationSpec,
initialValue: Float,
initialVelocity: Float = 0.0f
): DecayAnimation {
return DecayAnimation(
vectorizedDecayAnimationSpec = decayAnimationSpec.vectorize(Float.VectorConvertor),
convertor = Float.VectorConvertor,
initialValue = initialValue,
initialVelocityVector = Float.VectorConvertor.convertToVector(initialVelocity)
)
}
fun > DecayAnimationSpec.createAnimation(
convertor: VectorConvertor,
initialValue: S,
initialVelocityVector: V? = null
): DecayAnimation {
return DecayAnimation(
vectorizedDecayAnimationSpec = vectorize(convertor),
convertor = convertor,
initialValue = initialValue,
initialVelocityVector = initialVelocityVector
)
}
// ======== Internal ========
internal suspend fun > Animation.run(
startTimeMillis: Int,
executionState: AnimationStateImpl,
callback: ((executionState: AnimationStateImpl) -> Unit)? = null
) {
executionState.isRunning = true
var resolvedStartTimeMillis = startTimeMillis
if (startTimeMillis == UnspecifiedTimeMillis) {
callWithFrameMillis { frameTimeMillis ->
resolvedStartTimeMillis = frameTimeMillis
update(frameTimeMillis, frameTimeMillis, executionState, callback)
}
} else {
update(startTimeMillis, startTimeMillis, executionState, callback)
}
while (executionState.isRunning) {
callWithFrameMillis { frameTimeMillis ->
update(frameTimeMillis, resolvedStartTimeMillis, executionState, callback)
}
}
}
private suspend fun > Animation.callWithFrameMillis(
onFrame: (frameTimeMillis: Int) -> R
): R {
return if (!isInfinite) {
withFrameMillis { frameTimeMillisLong ->
val frameTimeMillis = frameTimeMillisLong.toInt()
onFrame(frameTimeMillis)
}
} else {
withInfiniteAnimationFrameMillis { frameTimeMillisLong ->
val frameTimeMillis = frameTimeMillisLong.toInt()
onFrame(frameTimeMillis)
}
}
}
private fun > Animation.update(
frameTimeMillis: Int,
startTimeMillis: Int,
executionState: AnimationStateImpl,
callback: ((executionState: AnimationStateImpl) -> Unit)? = null
) {
with(executionState) {
try {
lastFrameTimeMillis = frameTimeMillis
val playTimeMillis = frameTimeMillis - startTimeMillis
value = getValueFromMillis(playTimeMillis)
velocityVector = getVelocityVectorFromMillis(playTimeMillis)
if (playTimeMillis >= durationMillis) {
finishedTimeMillis = lastFrameTimeMillis
isRunning = false
}
callback?.invoke(this)
} catch (e: CancellationException) {
isRunning = false
throw e
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy