[go: nahoru, domu]

blob: 7b64a5768eb4a182fb330b3cf857ca6b80bc5a41 [file] [log] [blame]
/*
* Copyright 2019 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.foundation.animation
import androidx.compose.animation.core.AnimatedFloat
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.ExponentialDecay
import androidx.compose.animation.core.FloatDecayAnimationSpec
import androidx.compose.animation.core.OnAnimationEnd
import androidx.compose.animation.core.SpringSpec
import androidx.compose.animation.core.TargetAnimation
import androidx.compose.animation.core.fling
import androidx.compose.Composable
import androidx.compose.Immutable
import kotlin.math.abs
/**
* Class to specify fling behavior.
*
* When drag has ended, this class specifies what to do given the velocity
* with which drag ended and AnimatedFloat instance to perform fling on and read current value.
*
* Config that provides natural fling with customizable behaviour
* e.g fling friction or result target adjustment.
*
* If you want to only be able to drag/animate between predefined set of values,
* consider using [FlingConfig] function with anchors to generate such behaviour.
*
* @param decayAnimation the animation to control fling behaviour
* @param adjustTarget callback to be called at the start of fling
* so the final value for fling can be adjusted
*/
@Immutable
data class FlingConfig(
val decayAnimation: FloatDecayAnimationSpec,
val adjustTarget: (Float) -> TargetAnimation? = { null }
)
/**
* Default [FlingConfig] curve.
*
* @param adjustTarget callback to be called at the start of fling so the final value for fling
* can be adjusted
*/
@Composable
fun defaultFlingConfig(
adjustTarget: (Float) -> TargetAnimation? = { null }
): FlingConfig = actualFlingConfig(adjustTarget)
@Composable
internal expect fun actualFlingConfig(adjustTarget: (Float) -> TargetAnimation?): FlingConfig
/**
* Create fling config with anchors will make sure that after drag has ended,
* the value will be animated to one of the points from the predefined list.
*
* It takes velocity into account, though value will be animated to the closest
* point in provided list considering velocity.
*
* @param anchors set of anchors to animate to
* @param animationSpec animation which will be used for animations
* @param decayAnimation decay animation to be used to calculate closest point in the anchors set
* considering velocity.
*/
fun FlingConfig(
anchors: List<Float>,
animationSpec: AnimationSpec<Float> = SpringSpec(),
decayAnimation: FloatDecayAnimationSpec = ExponentialDecay()
): FlingConfig {
val adjustTarget: (Float) -> TargetAnimation? = { target ->
val point = anchors.minByOrNull { abs(it - target) }
val adjusted = point ?: target
TargetAnimation(adjusted, animationSpec)
}
return FlingConfig(decayAnimation, adjustTarget)
}
/**
* Starts a fling animation with the specified starting velocity and fling configuration.
*
* @param startVelocity Starting velocity of the fling animation
* @param config configuration that specifies fling behaviour
* @param onAnimationEnd callback to be invoked when fling finishes by decay
* or being interrupted by gesture input.
* Consider second boolean param "cancelled" to know what happened.
*/
fun AnimatedFloat.fling(
startVelocity: Float,
config: FlingConfig,
onAnimationEnd: OnAnimationEnd? = null
) {
fling(
startVelocity,
config.decayAnimation,
config.adjustTarget,
onAnimationEnd
)
}