Translating Objects along a Path (Part 2)

In the previous article, I explained how we can create a different path inside a canvas and animate shapes along that path.

In this session, I’ll explain how we can use the same concept to animate any compose view from one position to another on the screen.

Overview 📔

  1. Understanding UI hierarchy
  2. Getting source and end destination offset
  3. Creating TranslatingObject Compose function
  4. Animating Multiple Objects along the same path

Understanding UI hierarchy 🔍

  • TranslatingObject -> compose function containing Translating Emojis
  • MyHorizontalList -> compose function containing Lazy Row, the second item in this list is our start destination
  • MyHorizontalRow -> Row containing three boxes, the second box in this row is our end destination

All these compose functions are present inside a Box. with Box we can stack different views, so if we want to translate anything inside a box we just have to manage its offset value and z-index. Ill explain TranslatingObject compose function in a further section of this blog.

Getting source and end destination offset 🤌

In compose we have OnGloballyPositionedModifier, This modifier gives the final layout coordinates of any view after measuring is done. i have used the same modifier inside MyHorizontalList and MyHorizontalRow to collect start and end destination offset.

These offsets are stored as mutable state in MainScreen. we are also taking the size of view to find out the centre position of the view.

Modifier.onGloballyPositioned {
updateOffset(it.positionInRoot(), it.size)
}

Creating Translating Object Compose Function 💪

The TranslatingObject compose function follows a slotting pattern. it takes content as a compose lambda. This content lambda is wrapped within one Box composable. To translate this content we just have to change the offset value of this parent box

Now the Next step will be to create a smooth curve between the start and end destination.

val pos = FloatArray(2)
val tan = FloatArray(2)
val pathToAnimate = Path().apply {
val verticalDistance = endDestination.y - startDestination.y
val horizontalDistance = endDestination.x - startDestination.x
//cubic-bezier(.71,0,.29,1)
val c1x = startDestination.x + (0.71 * horizontalDistance)
val c1y = startDestination.y - (0.71 * verticalDistance)
val c2x = startDestination.x + (0.29 * horizontalDistance)
val c2y = endDestination.y
moveTo(startDestination.x, startDestination.y)
cubicTo(
x1 = c1x.toFloat(),
y1 = c1y.toFloat(),
x2 = c2x.toFloat(),
y2 = c2y.toFloat(),
x3 = endDestination.x,
y3 = endDestination.y
)
}

once we have the path object with us, we can use the PathMeasure function to calculate the position of any point along that path using getPosTan method. here we are using Animatable to update animateOffset value.

PathMeasure().apply {
setPath(pathToAnimate.asAndroidPath(), false)
pathAnimatable.animateTo(1f, tween(1500)) {
getPosTan(length * this.value, pos, tan)
animationOffset.value = Offset(pos[0], pos[1])
onProgressUpdateListener(this.value)
}
}

Overall following are the major components in TranslatingObject compose function

  • Box composable: wrapper around content lambda
  • pathToAnimate: smooth bezier curve between start and end destination
  • pathAnimatable: Animatable use to update animateOffset value
  • animateOffset: mutable offset which will be applied to BOX

Animating Multiple Objects along a Path 🤩

I have used “for loop” over TranslatingObject compose function. this will create n no of translating objects. now we just have to update n value based on our requirements

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store