0

I am trying to create a path animation of Svg in the desktop compose app.

fun SvgDocument.drawCompletedFills(buildPaths: List<BuildPath>, drawScope: DrawScope, canvas: Canvas, targetLength: Float) {
    var accumulatedLength = 0f
    val matrix = computeSkiaMatrix(drawScope)
    buildPaths.forEach { built ->
        val pathEnd = accumulatedLength + built.length
        if (targetLength >= pathEnd - COMPLETION_EPSILON) {
            val fillPaint = built.svgNode.createFillPaint()
            val transformedPath = built.skiaPath.transform(matrix)
            canvas.nativeCanvas.drawPath(transformedPath, fillPaint)
        }
        accumulatedLength += built.length
    }
}

above method runs the path animation , draw on the canvas but as soon as the next frame runs the previously ran animated path disappear.

Below method works and draw as intended

fun SvgDocument.drawPartialStrokes(
    buildPaths : List<BuildPath>,
    drawScope: DrawScope,
    canvas: Canvas,
    targetLength: Float,
) {

    var remainingLength = targetLength
    buildPaths.forEach { built ->
        if (remainingLength <= 0f) return@forEach
        //print("\n built path length: ${built.length} with targetLength = $targetLength and remainingLength = $remainingLength")
        val pathDrawLength = remainingLength.coerceAtMost(built.length)
        if (pathDrawLength > 0f) {
            val tracedPath = Path()
            val pathMeasure = PathMeasure().apply {
                setPath(built.skiaPath, false)
            }
            var lengthLeftOnPath = pathDrawLength
            do {
                val contourLength = pathMeasure.length
                if (lengthLeftOnPath <= 0f) break
                val segmentLength = lengthLeftOnPath.coerceAtMost(contourLength)
                if (segmentLength > 0f) {
                    pathMeasure.getSegment(
                        0f,
                        segmentLength,
                        tracedPath,
                        true
                    )
                }
                lengthLeftOnPath -= segmentLength
            } while (pathMeasure.nextContour())
            //val composePath = tracedPath.toComposePath(this, drawScope)
            val strokePaint = built.svgNode.createStrokePaint()
            canvas.nativeCanvas.drawPath(tracedPath.transform(computeSkiaMatrix(drawScope)),strokePaint)
        }
        remainingLength -= pathDrawLength
    }
}

both of the above methods is called in the below canvas

@Composable
fun SvgDocument.pathTrace(modifier: Modifier = Modifier, buildPaths: List<BuildPath>, totalLength: Float, progress: Float) {
    Canvas(modifier) {
        val clampedProgress = progress.coerceIn(0f, 1f)
        val targetLength = clampedProgress * totalLength
        drawIntoCanvas { canvas ->
            //canvas.nativeCanvas.saveLayer(null,null)
             drawPartialStrokes(buildPaths, this, canvas, targetLength)
            drawCompletedFills(buildPaths, this, canvas, targetLength)
            //canvas.nativeCanvas.restore()
        }
    }
}

if I comment out the drawCompletedFills method , partialSroke works and draw the path but if run both , animation flickers and only the last path stays on the screen

@Composable
fun SvgDocument.pathTraceSvgDocument(modifier: Modifier = Modifier, speedMs: Int = 10000, pauseMs: Int = 3000, easing: Easing = LinearEasing) {
    val buildPaths = remember(this) { buildPaths() }
    val totalLength = buildPaths.getTotalLength()
    val progress = remember { Animatable(0f) }

    LaunchedEffect(this, speedMs, pauseMs, easing) {
        while (true) {
            progress.snapTo(0f)
            progress.animateTo(targetValue = 1f, animationSpec = tween(durationMillis = speedMs, easing = easing))
            kotlinx.coroutines.delay(pauseMs.toLong())
        }
    }

    pathTrace(modifier = modifier.fillMaxSize(), buildPaths = buildPaths, totalLength = totalLength, progress = progress.value)
}

I have tried many things, including save the layer and restore; instead of skia, I tried using object of compose graphics , Paint and matrix stuff ( this uses skia internally i guess)
I took the help of AI as well , but all in vain.

for full code reference here is the link : Github repo

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.