|
|
|
|
@@ -0,0 +1,91 @@
|
|
|
|
|
package de.jeanlucmakiola.floret.identity
|
|
|
|
|
|
|
|
|
|
import androidx.compose.animation.ContentTransform
|
|
|
|
|
import androidx.compose.animation.EnterTransition
|
|
|
|
|
import androidx.compose.animation.ExitTransition
|
|
|
|
|
import androidx.compose.animation.expandVertically
|
|
|
|
|
import androidx.compose.animation.fadeIn
|
|
|
|
|
import androidx.compose.animation.fadeOut
|
|
|
|
|
import androidx.compose.animation.shrinkVertically
|
|
|
|
|
import androidx.compose.animation.slideInVertically
|
|
|
|
|
import androidx.compose.animation.togetherWith
|
|
|
|
|
import androidx.compose.foundation.lazy.LazyItemScope
|
|
|
|
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
|
|
|
|
import androidx.compose.material3.MaterialTheme
|
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The family's content transitions, driven by the active M3 Expressive motion
|
|
|
|
|
* scheme so every app reveals, collapses and re-lays-out the same way. Each
|
|
|
|
|
* honours [rememberReduceMotion]: when the user has asked the system to remove
|
|
|
|
|
* animations, the spatial movement is dropped and only a quick fade remains.
|
|
|
|
|
*
|
|
|
|
|
* Drawn from Calendula's motion helpers; pair [expandEnter] with [collapseExit]
|
|
|
|
|
* on an `AnimatedVisibility`, use [itemEnter] for content revealed once it
|
|
|
|
|
* resolves, [animateItemMotion] for `LazyColumn`/`LazyRow` row relayout, and
|
|
|
|
|
* [fadeThrough] to swap whole blocks with no spatial direction.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enter for a vertically-revealed section (expandable rows, inline fields):
|
|
|
|
|
* height grows from the top while fading in; a quick fade only under reduced
|
|
|
|
|
* motion. Pair with [collapseExit].
|
|
|
|
|
*/
|
|
|
|
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
|
|
|
@Composable
|
|
|
|
|
fun expandEnter(reduceMotion: Boolean = rememberReduceMotion()): EnterTransition {
|
|
|
|
|
val fade = fadeIn(MaterialTheme.motionScheme.fastEffectsSpec())
|
|
|
|
|
return if (reduceMotion) fade else expandVertically(MaterialTheme.motionScheme.fastSpatialSpec()) + fade
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Exit counterpart to [expandEnter]: shrink + fade, or fade only under reduced motion. */
|
|
|
|
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
|
|
|
@Composable
|
|
|
|
|
fun collapseExit(reduceMotion: Boolean = rememberReduceMotion()): ExitTransition {
|
|
|
|
|
val fade = fadeOut(MaterialTheme.motionScheme.fastEffectsSpec())
|
|
|
|
|
return if (reduceMotion) fade else shrinkVertically(MaterialTheme.motionScheme.fastSpatialSpec()) + fade
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enter for content revealed by an `AnimatedContent`/`AnimatedVisibility` (e.g.
|
|
|
|
|
* results once a query resolves): a gentle rise + fade; fade only under reduced
|
|
|
|
|
* motion.
|
|
|
|
|
*/
|
|
|
|
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
|
|
|
@Composable
|
|
|
|
|
fun itemEnter(reduceMotion: Boolean = rememberReduceMotion()): EnterTransition {
|
|
|
|
|
val fade = fadeIn(MaterialTheme.motionScheme.fastEffectsSpec())
|
|
|
|
|
return if (reduceMotion) fade else fade + slideInVertically(MaterialTheme.motionScheme.fastSpatialSpec()) { h -> h / 6 }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* `LazyItemScope.animateItem` wired to the app's motion scheme instead of
|
|
|
|
|
* Compose's default spring, so list rows fade/relocate consistently. Returns a
|
|
|
|
|
* bare [Modifier] under reduced motion so rows snap into place. Requires stable
|
|
|
|
|
* item keys.
|
|
|
|
|
*/
|
|
|
|
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
|
|
|
@Composable
|
|
|
|
|
fun LazyItemScope.animateItemMotion(reduceMotion: Boolean = rememberReduceMotion()): Modifier =
|
|
|
|
|
if (reduceMotion) {
|
|
|
|
|
Modifier
|
|
|
|
|
} else {
|
|
|
|
|
Modifier.animateItem(
|
|
|
|
|
fadeInSpec = MaterialTheme.motionScheme.fastEffectsSpec(),
|
|
|
|
|
placementSpec = MaterialTheme.motionScheme.fastSpatialSpec(),
|
|
|
|
|
fadeOutSpec = MaterialTheme.motionScheme.fastEffectsSpec(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cross-fade [ContentTransform] for swapping whole screens or blocks with no
|
|
|
|
|
* meaningful spatial direction (e.g. onboarding gates). Pure opacity, so it
|
|
|
|
|
* doubles as its own reduced-motion form.
|
|
|
|
|
*/
|
|
|
|
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
|
|
|
@Composable
|
|
|
|
|
fun fadeThrough(): ContentTransform {
|
|
|
|
|
val fade = MaterialTheme.motionScheme.fastEffectsSpec<Float>()
|
|
|
|
|
return fadeIn(fade).togetherWith(fadeOut(fade))
|
|
|
|
|
}
|