I want to animate stroke path but I couldn't find a way to animate as segmented. How can I draw below canvas as segmented like 0.7 of path from starting point in jetpack compose?
Canvas(modifier = Modifier.size(150.dp)) {
val path = Path()
val width: Float = size.width
val height: Float = size.height
path.apply {
moveTo(width / 2, height / 5)
cubicTo(
5 * width / 14, 0f,
0f, height / 15,
width / 28, 2 * height / 5
)
cubicTo(
width / 14, 2 * height / 3,
3 * width / 7, 5 * height / 6,
width / 2, height
)
cubicTo(
4 * width / 7, 5 * height / 6,
13 * width / 14, 2 * height / 3,
27 * width / 28, 2 * height / 5
)
cubicTo(
width, height / 15,
9 * width / 14, 0f,
width / 2, height / 5
)
}
drawPath(color = Color.Red, path = path, style = Stroke(8f))
}
You can do it with val pathMeasure by remember { mutableStateOf(PathMeasure()) }
and setting start and end distances with
pathWithProgress.reset()
pathMeasure.setPath(path, forceClosed = false)
pathMeasure.getSegment(
startDistance = 0f,
stopDistance = pathMeasure.length * progress,
pathWithProgress,
startWithMoveTo = true
)
The snippet below starts from 0.7f of Path and based on progress, i used a Slider for demonstration but it can easily be aniamted, it can be drawn to full path length.
Result
Code
@Preview
@Composable
private fun Test() {
var progress1 by remember {
mutableStateOf(0.7f)
}
val transition: InfiniteTransition = rememberInfiniteTransition(label = "heart animation")
// Infinite phase animation for PathEffect
val progress2 by transition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 1500,
easing = FastOutSlowInEasing
),
), label = "heart animation"
)
// This is the progress path which wis changed using path measure
val pathWithProgress by remember {
mutableStateOf(Path())
}
// using path
val pathMeasure by remember { mutableStateOf(PathMeasure()) }
val path = remember {
Path()
}
Column(
Modifier
.fillMaxSize()
.padding(20.dp)
) {
Text(text = "Progress1: $progress1")
Slider(
value = progress1,
onValueChange = { progress1 = it },
valueRange = 0.7f..1f
)
Canvas(modifier = Modifier.size(150.dp)) {
val width: Float = size.width
val height: Float = size.height
if (path.isEmpty) {
path.apply {
moveTo(width / 2, height / 5)
cubicTo(
5 * width / 14, 0f,
0f, height / 15,
width / 28, 2 * height / 5
)
cubicTo(
width / 14, 2 * height / 3,
3 * width / 7, 5 * height / 6,
width / 2, height
)
cubicTo(
4 * width / 7, 5 * height / 6,
13 * width / 14, 2 * height / 3,
27 * width / 28, 2 * height / 5
)
cubicTo(
width, height / 15,
9 * width / 14, 0f,
width / 2, height / 5
)
}
}
pathWithProgress.reset()
pathMeasure.setPath(path, forceClosed = false)
pathMeasure.getSegment(
startDistance = 0f,
stopDistance = pathMeasure.length * progress1,
pathWithProgress,
startWithMoveTo = true
)
drawPath(color = Color.Red, path = pathWithProgress, style = Stroke(8f))
pathWithProgress.reset()
pathMeasure.setPath(path, forceClosed = false)
pathMeasure.getSegment(
startDistance = 0f,
stopDistance = pathMeasure.length * progress2,
pathWithProgress,
startWithMoveTo = true
)
translate(left = 500f) {
drawPath(color = Color.Blue, path = pathWithProgress, style = Stroke(8f))
}
}
}
}