mirror of
https://github.com/BetterAndroid/FlexiUI.git
synced 2025-09-09 20:14:18 +08:00
refactor: decoupling animation and opened type interface
This commit is contained in:
@@ -59,29 +59,44 @@ import kotlin.math.abs
|
|||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
internal interface IProgressIndicatorStyle {
|
interface ProgressIndicatorStyle {
|
||||||
val strokeWidth: Dp
|
val strokeWidth: Dp
|
||||||
val strokeCap: StrokeCap
|
val strokeCap: StrokeCap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
interface ProgressIndicatorAnimation {
|
||||||
|
val duration: Int
|
||||||
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class CircularIndicatorStyle(
|
data class CircularIndicatorStyle(
|
||||||
override val strokeWidth: Dp,
|
override val strokeWidth: Dp,
|
||||||
override val strokeCap: StrokeCap,
|
override val strokeCap: StrokeCap,
|
||||||
val radius: Dp,
|
val radius: Dp,
|
||||||
val rotationDuration: Int,
|
val animation: CircularIndicatorAnimation
|
||||||
val rotationsPerCycle: Int,
|
) : ProgressIndicatorStyle
|
||||||
val startAngleOffset: Float,
|
|
||||||
val baseRotationAngle: Float,
|
|
||||||
val jumpRotationAngle: Float
|
|
||||||
) : IProgressIndicatorStyle
|
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class LinearIndicatorStyle(
|
data class LinearIndicatorStyle(
|
||||||
override val strokeWidth: Dp,
|
override val strokeWidth: Dp,
|
||||||
override val strokeCap: StrokeCap,
|
override val strokeCap: StrokeCap,
|
||||||
val width: Dp,
|
val width: Dp,
|
||||||
val animationDuration: Int,
|
val animation: LinearIndicatorAnimation
|
||||||
|
) : ProgressIndicatorStyle
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class CircularIndicatorAnimation(
|
||||||
|
override val duration: Int,
|
||||||
|
val rotationsPerCycle: Int,
|
||||||
|
val startAngleOffset: Float,
|
||||||
|
val baseRotationAngle: Float,
|
||||||
|
val jumpRotationAngle: Float
|
||||||
|
) : ProgressIndicatorAnimation
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class LinearIndicatorAnimation(
|
||||||
|
override val duration: Int,
|
||||||
val firstLineHeadDuration: Int,
|
val firstLineHeadDuration: Int,
|
||||||
val firstLineTailDuration: Int,
|
val firstLineTailDuration: Int,
|
||||||
val secondLineHeadDuration: Int,
|
val secondLineHeadDuration: Int,
|
||||||
@@ -90,7 +105,7 @@ data class LinearIndicatorStyle(
|
|||||||
val firstLineTailDelay: Int,
|
val firstLineTailDelay: Int,
|
||||||
val secondLineHeadDelay: Int,
|
val secondLineHeadDelay: Int,
|
||||||
val secondLineTailDelay: Int
|
val secondLineTailDelay: Int
|
||||||
) : IProgressIndicatorStyle
|
) : ProgressIndicatorAnimation
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class ProgressIndicatorColors(
|
data class ProgressIndicatorColors(
|
||||||
@@ -128,54 +143,54 @@ fun CircularProgressIndicator(
|
|||||||
val transition = rememberInfiniteTransition()
|
val transition = rememberInfiniteTransition()
|
||||||
val currentRotation by transition.animateValue(
|
val currentRotation by transition.animateValue(
|
||||||
initialValue = 0,
|
initialValue = 0,
|
||||||
style.rotationsPerCycle,
|
style.animation.rotationsPerCycle,
|
||||||
Int.VectorConverter,
|
Int.VectorConverter,
|
||||||
infiniteRepeatable(
|
infiniteRepeatable(
|
||||||
animation = tween(
|
animation = tween(
|
||||||
durationMillis = style.rotationDuration * style.rotationsPerCycle,
|
durationMillis = style.animation.duration * style.animation.rotationsPerCycle,
|
||||||
easing = LinearEasing
|
easing = LinearEasing
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val baseRotation by transition.animateFloat(
|
val baseRotation by transition.animateFloat(
|
||||||
initialValue = 0f,
|
initialValue = 0f,
|
||||||
style.baseRotationAngle,
|
style.animation.baseRotationAngle,
|
||||||
infiniteRepeatable(
|
infiniteRepeatable(
|
||||||
animation = tween(
|
animation = tween(
|
||||||
durationMillis = style.rotationDuration,
|
durationMillis = style.animation.duration,
|
||||||
easing = LinearEasing
|
easing = LinearEasing
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val headAndTailAnimationDuration = caleHeadAndTailAnimationDuration(style.rotationDuration)
|
val headAndTailAnimationDuration = caleHeadAndTailAnimationDuration(style.animation.duration)
|
||||||
val endAngle by transition.animateFloat(
|
val endAngle by transition.animateFloat(
|
||||||
initialValue = 0f,
|
initialValue = 0f,
|
||||||
style.jumpRotationAngle,
|
style.animation.jumpRotationAngle,
|
||||||
infiniteRepeatable(
|
infiniteRepeatable(
|
||||||
animation = keyframes {
|
animation = keyframes {
|
||||||
durationMillis = headAndTailAnimationDuration * 2
|
durationMillis = headAndTailAnimationDuration * 2
|
||||||
0f at 0 with CircularEasing
|
0f at 0 with CircularEasing
|
||||||
style.jumpRotationAngle at headAndTailAnimationDuration
|
style.animation.jumpRotationAngle at headAndTailAnimationDuration
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val startAngle by transition.animateFloat(
|
val startAngle by transition.animateFloat(
|
||||||
initialValue = 0f,
|
initialValue = 0f,
|
||||||
style.jumpRotationAngle,
|
style.animation.jumpRotationAngle,
|
||||||
infiniteRepeatable(
|
infiniteRepeatable(
|
||||||
animation = keyframes {
|
animation = keyframes {
|
||||||
durationMillis = headAndTailAnimationDuration * 2
|
durationMillis = headAndTailAnimationDuration * 2
|
||||||
0f at headAndTailAnimationDuration with CircularEasing
|
0f at headAndTailAnimationDuration with CircularEasing
|
||||||
style.jumpRotationAngle at durationMillis
|
style.animation.jumpRotationAngle at durationMillis
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Canvas(modifier.progressSemantics().size(diameter)) {
|
Canvas(modifier.progressSemantics().size(diameter)) {
|
||||||
drawCircularIndicatorBackground(colors.backgroundColor, stroke)
|
drawCircularIndicatorBackground(colors.backgroundColor, stroke)
|
||||||
val rotationAngleOffset = caleRotationAngleOffset(style.baseRotationAngle, style.jumpRotationAngle)
|
val rotationAngleOffset = caleRotationAngleOffset(style.animation.baseRotationAngle, style.animation.jumpRotationAngle)
|
||||||
val currentRotationAngleOffset = (currentRotation * rotationAngleOffset) % 360f
|
val currentRotationAngleOffset = (currentRotation * rotationAngleOffset) % 360f
|
||||||
val sweep = abs(endAngle - startAngle)
|
val sweep = abs(endAngle - startAngle)
|
||||||
val offset = style.startAngleOffset + currentRotationAngleOffset + baseRotation
|
val offset = style.animation.startAngleOffset + currentRotationAngleOffset + baseRotation
|
||||||
drawIndeterminateCircularIndicator(startAngle + offset, style.strokeWidth, diameter, sweep, colors.foregroundColor, stroke)
|
drawIndeterminateCircularIndicator(startAngle + offset, style.strokeWidth, diameter, sweep, colors.foregroundColor, stroke)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,9 +226,9 @@ fun LinearProgressIndicator(
|
|||||||
targetValue = 1f,
|
targetValue = 1f,
|
||||||
infiniteRepeatable(
|
infiniteRepeatable(
|
||||||
animation = keyframes {
|
animation = keyframes {
|
||||||
durationMillis = style.animationDuration
|
durationMillis = style.animation.duration
|
||||||
0f at style.firstLineHeadDelay with FirstLineHeadEasing
|
0f at style.animation.firstLineHeadDelay with FirstLineHeadEasing
|
||||||
1f at style.firstLineHeadDuration + style.firstLineHeadDelay
|
1f at style.animation.firstLineHeadDuration + style.animation.firstLineHeadDelay
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -222,9 +237,9 @@ fun LinearProgressIndicator(
|
|||||||
targetValue = 1f,
|
targetValue = 1f,
|
||||||
infiniteRepeatable(
|
infiniteRepeatable(
|
||||||
animation = keyframes {
|
animation = keyframes {
|
||||||
durationMillis = style.animationDuration
|
durationMillis = style.animation.duration
|
||||||
0f at style.firstLineTailDelay with FirstLineTailEasing
|
0f at style.animation.firstLineTailDelay with FirstLineTailEasing
|
||||||
1f at style.firstLineTailDuration + style.firstLineTailDelay
|
1f at style.animation.firstLineTailDuration + style.animation.firstLineTailDelay
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -233,9 +248,9 @@ fun LinearProgressIndicator(
|
|||||||
targetValue = 1f,
|
targetValue = 1f,
|
||||||
infiniteRepeatable(
|
infiniteRepeatable(
|
||||||
animation = keyframes {
|
animation = keyframes {
|
||||||
durationMillis = style.animationDuration
|
durationMillis = style.animation.duration
|
||||||
0f at style.secondLineHeadDelay with SecondLineHeadEasing
|
0f at style.animation.secondLineHeadDelay with SecondLineHeadEasing
|
||||||
1f at style.secondLineHeadDuration + style.secondLineHeadDelay
|
1f at style.animation.secondLineHeadDuration + style.animation.secondLineHeadDelay
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -244,9 +259,9 @@ fun LinearProgressIndicator(
|
|||||||
targetValue = 1f,
|
targetValue = 1f,
|
||||||
infiniteRepeatable(
|
infiniteRepeatable(
|
||||||
animation = keyframes {
|
animation = keyframes {
|
||||||
durationMillis = style.animationDuration
|
durationMillis = style.animation.duration
|
||||||
0f at style.secondLineTailDelay with SecondLineTailEasing
|
0f at style.animation.secondLineTailDelay with SecondLineTailEasing
|
||||||
1f at style.secondLineTailDuration + style.secondLineTailDelay
|
1f at style.animation.secondLineTailDuration + style.animation.secondLineTailDelay
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -388,11 +403,13 @@ private fun defaultCircularIndicatorStyle() = CircularIndicatorStyle(
|
|||||||
strokeWidth = DefaultIndicatorStrokeWidth,
|
strokeWidth = DefaultIndicatorStrokeWidth,
|
||||||
strokeCap = StrokeCap.Round,
|
strokeCap = StrokeCap.Round,
|
||||||
radius = DefaultCircularIndicatorRadius,
|
radius = DefaultCircularIndicatorRadius,
|
||||||
rotationDuration = DefaultRotationDuration,
|
animation = CircularIndicatorAnimation(
|
||||||
rotationsPerCycle = DefaultRotationsPerCycle,
|
duration = DefaultRotationDuration,
|
||||||
startAngleOffset = DefaultStartAngleOffset,
|
rotationsPerCycle = DefaultRotationsPerCycle,
|
||||||
baseRotationAngle = DefaultBaseRotationAngle,
|
startAngleOffset = DefaultStartAngleOffset,
|
||||||
jumpRotationAngle = DefaultJumpRotationAngle
|
baseRotationAngle = DefaultBaseRotationAngle,
|
||||||
|
jumpRotationAngle = DefaultJumpRotationAngle
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -401,15 +418,17 @@ private fun defaultLinearIndicatorStyle() = LinearIndicatorStyle(
|
|||||||
strokeWidth = DefaultIndicatorStrokeWidth,
|
strokeWidth = DefaultIndicatorStrokeWidth,
|
||||||
strokeCap = StrokeCap.Round,
|
strokeCap = StrokeCap.Round,
|
||||||
width = DefaultLinearIndicatorWidth,
|
width = DefaultLinearIndicatorWidth,
|
||||||
animationDuration = DefaultLinearAnimationDuration,
|
animation = LinearIndicatorAnimation(
|
||||||
firstLineHeadDuration = DefaultFirstLineHeadDuration,
|
duration = DefaultLinearAnimationDuration,
|
||||||
firstLineTailDuration = DefaultFirstLineTailDuration,
|
firstLineHeadDuration = DefaultFirstLineHeadDuration,
|
||||||
secondLineHeadDuration = DefaultSecondLineHeadDuration,
|
firstLineTailDuration = DefaultFirstLineTailDuration,
|
||||||
secondLineTailDuration = DefaultSecondLineTailDuration,
|
secondLineHeadDuration = DefaultSecondLineHeadDuration,
|
||||||
firstLineHeadDelay = DefaultFirstLineHeadDelay,
|
secondLineTailDuration = DefaultSecondLineTailDuration,
|
||||||
firstLineTailDelay = DefaultFirstLineTailDelay,
|
firstLineHeadDelay = DefaultFirstLineHeadDelay,
|
||||||
secondLineHeadDelay = DefaultSecondLineHeadDelay,
|
firstLineTailDelay = DefaultFirstLineTailDelay,
|
||||||
secondLineTailDelay = DefaultSecondLineTailDelay
|
secondLineHeadDelay = DefaultSecondLineHeadDelay,
|
||||||
|
secondLineTailDelay = DefaultSecondLineTailDelay
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun caleRotationAngleOffset(baseRotationAngle: Float, jumpRotationAngle: Float) = (baseRotationAngle + jumpRotationAngle) % 360f
|
private fun caleRotationAngleOffset(baseRotationAngle: Float, jumpRotationAngle: Float) = (baseRotationAngle + jumpRotationAngle) % 360f
|
||||||
|
Reference in New Issue
Block a user