commonMain.androidx.compose.material3.Elevation.kt Maven / Gradle / Ivy
/*
* Copyright 2021 The Android Open Source Project
*
* 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 androidx.compose.material3
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.TweenSpec
import androidx.compose.foundation.interaction.DragInteraction
import androidx.compose.foundation.interaction.FocusInteraction
import androidx.compose.foundation.interaction.HoverInteraction
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.ui.unit.Dp
/**
* Animates the [Dp] value of [this] between [from] and [to] [Interaction]s, to [target]. The
* [AnimationSpec] used depends on the values for [from] and [to], see
* [ElevationDefaults.incomingAnimationSpecForInteraction] and
* [ElevationDefaults.outgoingAnimationSpecForInteraction] for more details.
*
* @param target the [Dp] target elevation for this component, corresponding to the elevation
* desired for the [to] state.
* @param from the previous [Interaction] that was used to calculate elevation. `null` if there
* was no previous [Interaction], such as when the component is in its default state.
* @param to the [Interaction] that this component is moving to, such as [PressInteraction.Press]
* when this component is being pressed. `null` if this component is moving back to its default
* state.
*/
internal suspend fun Animatable.animateElevation(
target: Dp,
from: Interaction? = null,
to: Interaction? = null
) {
val spec = when {
// Moving to a new state
to != null -> ElevationDefaults.incomingAnimationSpecForInteraction(to)
// Moving to default, from a previous state
from != null -> ElevationDefaults.outgoingAnimationSpecForInteraction(from)
// Loading the initial state, or moving back to the baseline state from a disabled /
// unknown state, so just snap to the final value.
else -> null
}
if (spec != null) animateTo(target, spec) else snapTo(target)
}
/**
* Contains default [AnimationSpec]s used for animating elevation between different [Interaction]s.
*
* Typically you should use [animateElevation] instead, which uses these [AnimationSpec]s
* internally. [animateElevation] in turn is used by the defaults for cards and buttons.
*
* @see animateElevation
*/
private object ElevationDefaults {
/**
* Returns the [AnimationSpec]s used when animating elevation to [interaction], either from a
* previous [Interaction], or from the default state. If [interaction] is unknown, then
* returns `null`.
*
* @param interaction the [Interaction] that is being animated to
*/
fun incomingAnimationSpecForInteraction(interaction: Interaction): AnimationSpec? {
return when (interaction) {
is PressInteraction.Press -> DefaultIncomingSpec
is DragInteraction.Start -> DefaultIncomingSpec
is HoverInteraction.Enter -> DefaultIncomingSpec
is FocusInteraction.Focus -> DefaultIncomingSpec
else -> null
}
}
/**
* Returns the [AnimationSpec]s used when animating elevation away from [interaction], to the
* default state. If [interaction] is unknown, then returns `null`.
*
* @param interaction the [Interaction] that is being animated away from
*/
fun outgoingAnimationSpecForInteraction(interaction: Interaction): AnimationSpec? {
return when (interaction) {
is PressInteraction.Press -> DefaultOutgoingSpec
is DragInteraction.Start -> DefaultOutgoingSpec
is HoverInteraction.Enter -> HoveredOutgoingSpec
is FocusInteraction.Focus -> DefaultOutgoingSpec
else -> null
}
}
}
private val OutgoingSpecEasing: Easing = CubicBezierEasing(0.40f, 0.00f, 0.60f, 1.00f)
private val DefaultIncomingSpec = TweenSpec(
durationMillis = 120,
easing = FastOutSlowInEasing
)
private val DefaultOutgoingSpec = TweenSpec(
durationMillis = 150,
easing = OutgoingSpecEasing
)
private val HoveredOutgoingSpec = TweenSpec(
durationMillis = 120,
easing = OutgoingSpecEasing
)