Updated on 2026-08-14
This commit is contained in:
parent
ec9bd02e35
commit
8c6a582d4a
9 changed files with 195 additions and 48 deletions
|
|
@ -3,11 +3,11 @@
|
|||
package com.tangem.core.ui.components.progressbar
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -39,7 +39,7 @@ import kotlin.math.abs
|
|||
* @param strokeCap stroke cap to use for the ends of this progress indicator
|
||||
*/
|
||||
@Composable
|
||||
fun LinearProgressIndicator(
|
||||
fun TangemLinearProgressIndicator(
|
||||
progress: () -> Float,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = TangemTheme.colors.text.accent,
|
||||
|
|
@ -61,6 +61,99 @@ fun LinearProgressIndicator(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress indicators loading status with infinite animation
|
||||
*
|
||||
* @param modifier the [Modifier] to be applied to this progress indicator
|
||||
* @param color The color of the progress indicator.
|
||||
* @param backgroundColor The color of the background behind the indicator, visible when the
|
||||
* progress has not reached that area of the overall indicator yet.
|
||||
* @param strokeCap stroke cap to use for the ends of this progress indicator
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun TangemLinearProgressIndicator(
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = TangemTheme.colors.text.accent,
|
||||
backgroundColor: Color = TangemTheme.colors.background.tertiary,
|
||||
strokeCap: StrokeCap = StrokeCap.Round,
|
||||
) {
|
||||
val infiniteTransition = rememberInfiniteTransition()
|
||||
val firstLineHead by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = LINEAR_ANIMATION_DURATION
|
||||
0f at FIRST_LINE_HEAD_DELAY using FIRST_LINE_HEAD_EASING
|
||||
1f at FIRST_LINE_HEAD_DURATION + FIRST_LINE_HEAD_DELAY
|
||||
},
|
||||
),
|
||||
)
|
||||
val firstLineTail by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = LINEAR_ANIMATION_DURATION
|
||||
0f at FIRST_LINE_TAIL_DELAY using FIRST_LINE_TAIL_EASING
|
||||
1f at FIRST_LINE_TAIL_DURATION + FIRST_LINE_TAIL_DELAY
|
||||
},
|
||||
),
|
||||
)
|
||||
val secondLineHead by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = LINEAR_ANIMATION_DURATION
|
||||
0f at SECOND_LINE_HEAD_DELAY using SECOND_LINE_HEAD_EASING
|
||||
1f at SECOND_LINE_HEAD_DURATION + SECOND_LINE_HEAD_DELAY
|
||||
},
|
||||
),
|
||||
)
|
||||
val secondLineTail by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = LINEAR_ANIMATION_DURATION
|
||||
0f at SECOND_LINE_TAIL_DELAY using SECOND_LINE_TAIL_EASING
|
||||
1f at SECOND_LINE_TAIL_DURATION + SECOND_LINE_TAIL_DELAY
|
||||
},
|
||||
),
|
||||
)
|
||||
Canvas(
|
||||
modifier
|
||||
.increaseSemanticsBounds()
|
||||
.semantics(mergeDescendants = true) {
|
||||
progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate
|
||||
}
|
||||
.size(height = 4.dp, width = 24.dp),
|
||||
) {
|
||||
val strokeWidth = size.height
|
||||
drawLinearIndicatorBackground(backgroundColor, strokeWidth, strokeCap)
|
||||
if (firstLineHead - firstLineTail > 0) {
|
||||
drawLinearIndicator(
|
||||
firstLineHead,
|
||||
firstLineTail,
|
||||
color,
|
||||
strokeWidth,
|
||||
strokeCap,
|
||||
)
|
||||
}
|
||||
if ((secondLineHead - secondLineTail) > 0) {
|
||||
drawLinearIndicator(
|
||||
secondLineHead,
|
||||
secondLineTail,
|
||||
color,
|
||||
strokeWidth,
|
||||
strokeCap,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Modifier.increaseSemanticsBounds(): Modifier {
|
||||
val padding = 10.dp
|
||||
return this
|
||||
|
|
@ -129,16 +222,51 @@ private fun DrawScope.drawLinearIndicator(
|
|||
private fun DrawScope.drawLinearIndicatorBackground(color: Color, strokeWidth: Float, strokeCap: StrokeCap) =
|
||||
drawLinearIndicator(0f, 1f, color, strokeWidth, strokeCap)
|
||||
|
||||
// Indeterminate linear indicator transition specs
|
||||
// Total duration for one cycle
|
||||
private const val LINEAR_ANIMATION_DURATION = 1800
|
||||
|
||||
// Duration of the head and tail animations for both lines
|
||||
private const val FIRST_LINE_HEAD_DURATION = 750
|
||||
private const val FIRST_LINE_TAIL_DURATION = 850
|
||||
private const val SECOND_LINE_HEAD_DURATION = 567
|
||||
private const val SECOND_LINE_TAIL_DURATION = 533
|
||||
|
||||
// Delay before the start of the head and tail animations for both lines
|
||||
private const val FIRST_LINE_HEAD_DELAY = 0
|
||||
private const val FIRST_LINE_TAIL_DELAY = 333
|
||||
private const val SECOND_LINE_HEAD_DELAY = 1000
|
||||
private const val SECOND_LINE_TAIL_DELAY = 1267
|
||||
|
||||
private val FIRST_LINE_HEAD_EASING = CubicBezierEasing(0.2f, 0f, 0.8f, 1f)
|
||||
private val FIRST_LINE_TAIL_EASING = CubicBezierEasing(0.4f, 0f, 1f, 1f)
|
||||
private val SECOND_LINE_HEAD_EASING = CubicBezierEasing(0f, 0f, 0.65f, 1f)
|
||||
private val SECOND_LINE_TAIL_EASING = CubicBezierEasing(0.1f, 0f, 0.45f, 1f)
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun LinearProgressIndicator_Preview() {
|
||||
TangemThemePreview {
|
||||
LinearProgressIndicator(
|
||||
progress = { 0.5f },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.secondary)
|
||||
.padding(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
TangemLinearProgressIndicator(
|
||||
progress = { 0.6f },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||
)
|
||||
TangemLinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
Loading…
Add table
Add a link
Reference in a new issue