net.peanuuutz.fork.ui.animation.AnimateAsState.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.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import net.peanuuutz.fork.ui.animation.spec.target.DefaultColorAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.DefaultFloatAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.DefaultFloatOffsetAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.DefaultFloatSizeAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.DefaultIntAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.DefaultIntOffsetAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.DefaultIntSizeAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.DefaultRectAnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.composite.AnimationSpec
import net.peanuuutz.fork.ui.animation.spec.target.composite.SpringSpec
import net.peanuuutz.fork.ui.animation.vector.AnimationVector
import net.peanuuutz.fork.ui.animation.vector.VectorConvertor
import net.peanuuutz.fork.ui.ui.draw.shape.Rect
import net.peanuuutz.fork.ui.ui.unit.FloatOffset
import net.peanuuutz.fork.ui.ui.unit.FloatSize
import net.peanuuutz.fork.ui.ui.unit.IntOffset
import net.peanuuutz.fork.ui.ui.unit.IntSize
import net.peanuuutz.fork.util.common.Color
@Composable
fun animateFloatAsState(
targetValue: Float,
animationSpec: AnimationSpec = DefaultFloatAnimationSpec,
onFinish: ((Float) -> Unit)? = null
): State {
return animateValueAsState(
convertor = Float.VectorConvertor,
targetValue = targetValue,
animationSpec = animationSpec,
onFinish = onFinish
)
}
@Composable
fun animateIntAsState(
targetValue: Int,
animationSpec: AnimationSpec = DefaultIntAnimationSpec,
onFinish: ((Int) -> Unit)? = null
): State {
return animateValueAsState(
convertor = Int.VectorConvertor,
targetValue = targetValue,
animationSpec = animationSpec,
onFinish = onFinish
)
}
@Composable
fun animateFloatOffsetAsState(
targetValue: FloatOffset,
animationSpec: AnimationSpec = DefaultFloatOffsetAnimationSpec,
onFinish: ((FloatOffset) -> Unit)? = null
): State {
return animateValueAsState(
convertor = FloatOffset.VectorConvertor,
targetValue = targetValue,
animationSpec = animationSpec,
onFinish = onFinish
)
}
@Composable
fun animateIntOffsetAsState(
targetValue: IntOffset,
animationSpec: AnimationSpec = DefaultIntOffsetAnimationSpec,
onFinish: ((IntOffset) -> Unit)? = null
): State {
return animateValueAsState(
convertor = IntOffset.VectorConvertor,
targetValue = targetValue,
animationSpec = animationSpec,
onFinish = onFinish
)
}
@Composable
fun animateFloatSizeAsState(
targetValue: FloatSize,
animationSpec: AnimationSpec = DefaultFloatSizeAnimationSpec,
onFinish: ((FloatSize) -> Unit)? = null
): State {
return animateValueAsState(
convertor = FloatSize.VectorConvertor,
targetValue = targetValue,
animationSpec = animationSpec,
onFinish = onFinish
)
}
@Composable
fun animateIntSizeAsState(
targetValue: IntSize,
animationSpec: AnimationSpec = DefaultIntSizeAnimationSpec,
onFinish: ((IntSize) -> Unit)? = null
): State {
return animateValueAsState(
convertor = IntSize.VectorConvertor,
targetValue = targetValue,
animationSpec = animationSpec,
onFinish = onFinish
)
}
@Composable
fun animateColorAsState(
targetValue: Color,
animationSpec: AnimationSpec = DefaultColorAnimationSpec,
onFinish: ((Color) -> Unit)? = null
): State {
return animateValueAsState(
convertor = Color.VectorConvertor,
targetValue = targetValue,
animationSpec = animationSpec,
onFinish = onFinish
)
}
@Composable
fun animateRectAsState(
targetValue: Rect,
animationSpec: AnimationSpec = DefaultRectAnimationSpec,
onFinish: ((Rect) -> Unit)? = null
): State {
return animateValueAsState(
convertor = Rect.VectorConvertor,
targetValue = targetValue,
animationSpec = animationSpec,
onFinish = onFinish
)
}
@Composable
fun > animateValueAsState(
convertor: VectorConvertor,
targetValue: S,
animationSpec: AnimationSpec = SpringSpec(),
onFinish: ((S) -> Unit)? = null
): State {
val animatable = remember { Animatable(targetValue, convertor) }
val onFinishRemembered by rememberUpdatedState(onFinish)
val animationSpecRemembered by rememberUpdatedState(animationSpec)
val channel = remember { Channel(capacity = Channel.CONFLATED) }
LaunchedEffect(Unit) {
for (attemptTargetValue in channel) {
val candidateTargetValue = channel.tryReceive().getOrNull() ?: attemptTargetValue
if (candidateTargetValue != animatable.targetValue) {
launch {
animatable.animateTo(candidateTargetValue, animationSpecRemembered)
onFinishRemembered?.invoke(animatable.value)
}
}
}
}
SideEffect {
channel.trySend(targetValue)
}
return animatable.animationState
}