Skip to content

Instantly share code, notes, and snippets.

@cbeyls
Created April 27, 2025 17:04
Show Gist options
  • Save cbeyls/7845b603370d238e14775d6b142db7fa to your computer and use it in GitHub Desktop.
Save cbeyls/7845b603370d238e14775d6b142db7fa to your computer and use it in GitHub Desktop.
Looping animated vector image for Compose (experimental)
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package be.digitalia.compose.animation.graphics
import androidx.compose.animation.core.SeekableTransitionState
import androidx.compose.animation.core.rememberTransition
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.animation.graphics.vector.StateVectorConfig
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.RenderVectorGroup
import androidx.compose.ui.graphics.vector.VectorComposable
import androidx.compose.ui.graphics.vector.VectorConfig
import androidx.compose.ui.graphics.vector.VectorGroup
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.util.fastForEach
@Composable
fun rememberLoopingAnimatedVectorPainter(
animatedImageVector: AnimatedImageVector
): Painter {
return rememberLoopingAnimatedVectorPainter(animatedImageVector) { group, overrides ->
RenderVectorGroup(group, overrides)
}
}
@Composable
private fun rememberLoopingAnimatedVectorPainter(
animatedImageVector: AnimatedImageVector,
render: @Composable @VectorComposable (VectorGroup, Map<String, VectorConfig>) -> Unit
): Painter {
return rememberVectorPainter(
defaultWidth = animatedImageVector.imageVector.defaultWidth,
defaultHeight = animatedImageVector.imageVector.defaultHeight,
viewportWidth = animatedImageVector.imageVector.viewportWidth,
viewportHeight = animatedImageVector.imageVector.viewportHeight,
name = animatedImageVector.imageVector.name,
tintColor = animatedImageVector.imageVector.tintColor,
tintBlendMode = animatedImageVector.imageVector.tintBlendMode,
autoMirror = true
) { _, _ ->
val transitionState = remember { SeekableTransitionState(false) }
val transition = rememberTransition(transitionState, label = animatedImageVector.imageVector.name)
val map = mutableMapOf<String, StateVectorConfig>()
animatedImageVector.targets.fastForEach { target ->
val config = target.animator.createVectorConfig(transition, animatedImageVector.totalDuration)
val currentConfig = map[target.name]
if (currentConfig != null) {
currentConfig.merge(config)
} else {
map[target.name] = config
}
}
render(animatedImageVector.imageVector.root, map)
LaunchedEffect(transitionState) {
while (true) {
transitionState.animateTo(true)
transitionState.snapTo(false)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment