-
Notifications
You must be signed in to change notification settings - Fork 174
Introduction to MotionLayout in Compose
MotionLayout's core engine allows you to interpolate between two constraintSet With Transitions and keyframes controlling the transitions.
<SideNote:
The full MotionLayout has many features (See ConstraintLayout 2.1)
The first release of MotionLayout in ConstraintLayoutCompose 1.0 had a limited subset using the json syntax.
The current 1.1.0 has DSL and JSON support
The hope is that users will provide feedback to allow us to tailor the API to developer needs.
MotionLayout for Compose takes on of:
- Two ConstraintsSet (start & end), a Transition, and progress
- A MotionScene and progress
See Introduction to ConstraintLayout for more details on ConstraintSets
fun MotionLayout(
start: ConstraintSet,
end: ConstraintSet,
transition: androidx.constraintlayout.compose.Transition? = null,
progress: Float,
debug: EnumSet<MotionLayoutDebugFlags> = EnumSet.of(MotionLayoutDebugFlags.NONE),
modifier: Modifier = Modifier,
optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
crossinline content: @Composable MotionLayoutScope.() -> Unit
) {}
fun MotionLayout(
motionScene: MotionScene,
progress: Float,
debug: EnumSet<MotionLayoutDebugFlags> = EnumSet.of(MotionLayoutDebugFlags.NONE),
modifier: Modifier = Modifier,
optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
crossinline content: @Composable() (MotionLayoutScope.() -> Unit),
) {}
e.g:
// DSL
transition = transition(name="forward", to=startState, from=nextState) {
keyAttributes(slot0, slot1, slot2) {
frame(50) {
scaleX = .3f
scaleY = .3f
}
}
}
// JSON
transition = Transition("""
{
KeyFrames: {
KeyPositions: [
{
target: ['a'],
frames: [50],
percentX: [0.8],
percentY: [0.8]
}
]
}
}
Details on Transitions in json
MotionScene only supports a JSON syntax in 1.0.0
MotionScene supports DSL & JSON in 1.1.0
@Preview(group = "motion8")
@Composable
public fun AttributesRotationXY() {
var animateToEnd by remember { mutableStateOf(false) }
val progress by animateFloatAsState(
targetValue = if (animateToEnd) 1f else 0f,
animationSpec = tween(6000)
)
Column {
MotionLayout(
modifier = Modifier
.fillMaxWidth()
.height(400.dp)
.background(Color.White),
motionScene = MotionScene("""{
ConstraintSets: { // all ConstraintSets
start: { // constraint set id = "start"
a: { // Constraint for widget id='a'
width: 40,
height: 40,
start: ['parent', 'start', 16],
bottom: ['parent', 'bottom', 16]
}
},
end: { // constraint set id = "start"
a: {
width: 40,
height: 40,
//rotationZ: 390,
end: ['parent', 'end', 16],
top: ['parent', 'top', 16]
}
}
},
Transitions: { // All Transitions in here
default: { // transition named 'default'
from: 'start', // go from Transition "start"
to: 'end', // go to Transition "end"
pathMotionArc: 'startHorizontal', // move in arc
KeyFrames: { // All keyframes go here
KeyAttributes: [ // keyAttributes key frames go here
{
target: ['a'], // This keyAttributes group target id "a"
frames: [25,50,75], // 3 points on progress 25% , 50% and 75%
rotationX: [0, 45, 60], // the rotationX angles are a spline from 0,0,45,60,0
rotationY: [60, 45, 0], // the rotationX angles are a spline from 0,60,45,0,0
}
]
}
}
}
}"""),
debug = EnumSet.of(MotionLayoutDebugFlags.SHOW_ALL),
progress = progress) {
Box(modifier = Modifier
.layoutId("a")
.background(Color.Red))
}
Button(onClick = { animateToEnd = !animateToEnd }) {
Text(text = "Run")
}
}
}
(new for 1.1.0-alpha0)
OnSwipe allows you to easily drive a transition from a drag gesture. In the typical Structure of a MotionLayout
ms = MotionScene() {
start = constraintSet() {
}
end = constraintSet() {
}
transition(start,end,"default") {
onSwipe = OnSwipe(
anchor = box,
direction = SwipeDirection.Right,
side = SwipeSide.Left,
mode = mode,
onTouchUp = touchUp
)
}
MotionLayout( motionSceen = ms) {...}
Adding this section allows you to control gestures with swiping.
-
anchor
- The Composable you wish your finger to track -
side
- The side of the anchor Composable your finger will track -
direction
- direction of you motion -
scale
- a scale factor to magnify your motion -
maxVelocity
- The maximum speed you can make progress at (progress per second) -
maxAccel
- The maximum the system can accelerate progress changes (progress per second per second) -
mode
- Spring or velocity mode. -
touchUp
- What happens on lifting your finger -
springMass
- The mass of the Spring in spring mode -
springStiffness
- The stiffness of the spring in spring mode -
springDamping
- The damping of the spring spring mode -
stopThreshold
- The speed threshold below which the animation is ended in spring mode -
springBoundary
- what happens when the spring hits the target (0 or 1) overshoot or bounce
Spring mode simulates a spring pulling to the destination (0 or 1) Velocity mode brings you to rest at the destination
- in under the duration time
- keeping the velocity below maxVelocity
- moving such that it is continuous in change of velocity (no sudden jumps in speed)
- with a maximum acceleration
(defaults
)
- anchor : 'id'
-
side : "
top
", 'left', 'right', 'bottom', 'middle', 'start", 'end' -
direction :
up
", "down", "left", "right", "start", "end" -
scale :
1.0
-
maxVelocity :
4.0
-
maxAccel :
1.2
-
mode : "
velocity
", "spring" -
touchUp : "
autocomplete
", "toStart", "toEnd", "stop", "decelerate", "decelerateComplete", "neverCompleteStart", "neverCompleteEnd" -
springMass :
1.0
-
springStiffness :
400
-
springDamping :
10
-
stopThreshold :
0.01
-
springBoundary : "
overshoot
", "bounceStart", "bounceEnd", "bounceBoth"
Notes: It sometimes does not matter if you specify opposite directions (such as up or down) when you specify an anchor because anchor maps the drag to the direct motion of the anchor in that direction.