commonMain.com.tunjid.composables.ui.ContentScaleInterpolation.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of composables Show documentation
Show all versions of composables Show documentation
A collection of utility composable functions
The newest version!
package com.tunjid.composables.ui
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.animate
import androidx.compose.animation.core.spring
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.ScaleFactor
import androidx.compose.ui.layout.lerp
/**
* Returns a [ContentScale] that animates smoothly between the initial value this method
* was composed with and subsequent invocations. This allows for preserving
* visual continuity across dynamic contexts like shared elements.
*
* @param animationSpec The [FiniteAnimationSpec] to be used for the animation.
* Note that changes to [animationSpec] while the animation is in progress have no effect.
* They will only be applied on the next [ContentScale] change to preserve animation smoothness.
*/
@Composable
fun ContentScale.animate(
animationSpec: FiniteAnimationSpec = spring(),
): ContentScale {
val updatedAnimationSpec by rememberUpdatedState(animationSpec)
var interpolation by remember {
mutableFloatStateOf(1f)
}
var previousScale by remember {
mutableStateOf(this)
}
val currentScale by remember {
mutableStateOf(this)
}.apply {
if (value != this@animate) {
previousScale = if (interpolation == 1f) {
// Value has changed, trigger an animation
value
} else {
// A previous animation has been interrupted. Capture the present state,
// and restart the animation.
lerp(
fraction = interpolation,
start = previousScale,
stop = value,
)
}
// Reset the interpolation
interpolation = 0f
}
// Set the current value, this will also stop any call to lerp above from recomposing
value = this@animate
}
LaunchedEffect(currentScale) {
animate(
initialValue = 0f,
targetValue = 1f,
animationSpec = updatedAnimationSpec,
block = { progress, _ ->
interpolation = progress
},
)
}
return remember {
object : ContentScale {
override fun computeScaleFactor(
srcSize: Size,
dstSize: Size,
): ScaleFactor {
val start = previousScale.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize,
)
val stop = currentScale.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize,
)
return when (start) {
stop -> stop
else -> lerp(
start = start,
stop = stop,
fraction = interpolation,
)
}
}
}
}
}
/**
* Linearly interpolate between two [ContentScale] parameters
*
* The [fraction] argument represents position on the timeline, with 0.0 meaning
* that the interpolation has not started, returning [start] (or something
* equivalent to [start]), 1.0 meaning that the interpolation has finished,
* returning [stop] (or something equivalent to [stop]), and values in between
* meaning that the interpolation is at the relevant point on the timeline
* between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
* 1.0, so negative values and values greater than 1.0 are valid (and can
* easily be generated by curves).
*/
@Composable
fun lerp(
start: ContentScale,
stop: ContentScale,
fraction: Float,
): ContentScale {
val updatedFraction by rememberUpdatedState(fraction)
return remember {
object : ContentScale {
override fun computeScaleFactor(
srcSize: Size,
dstSize: Size,
): ScaleFactor =
lerp(
start = start.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize,
),
stop = stop.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize,
),
fraction = updatedFraction,
)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy