-
Notifications
You must be signed in to change notification settings - Fork 174
Compose MotionLayout JSON Syntax
This is an early release of MotionLayout. Our first emphasis is on developing the JSON syntax. Eventually we will support direct implementation in Kotlin. The Functionality operates identically to the 2.1 MotionLayout equivalent.
{
Header:{}
Design:{}
Variables:{}
ConstraintSets:{}
Transitions: {
// Transition Named default (special name)
default:{
KeyFrames:{
KeyPositions: {}
KeyAttributes: {}
KeyCycles: {}
}
}
}
}
This allows you to export the current string for Realtime remote editing via Link
Header:{
exportAs: 'main screen'
}
This allows you to create widgets (currently only text and button)
Design: {
toto: {
type: 'button',
text: 'plop'
}
}
This allows you to define variables that can be used multiple times across the entire string.
Variables: {
simple1: 8
increment1 : { from: 90, step: 10 }
increment2 : { from: 1, to: 36, prefix: 'h' },
fromtag1: { tag: 'box' }
},
- simple1 can be used in many places and will be replaced by 8
- increment1 value will change (increase by 10) each time it is used (especially useful in combination with Generate: )
- increment2 will have the values h1 to h36
- fromtag1 will map to all ids of the views created with with a tag.
For example if composables were created like this:
for (i in 1..36) {
Box(modifier = Modifier
.layoutId("id$i", "box")
.background(colors[i % colors.size]))
}
fromtag1 will be [id1,id2,...id36]
Generate section can be used to generate a collection of Constraint objects when used with variables. For example:
{
Variables: {
angle: { from: 0, step: 10 },
rotation: { from: 'startRotation', step: 10 },
distance: 100,
mylist: { tag: 'box' }
},
Generate: {
mylist: {
width: 200,
height: 40,
circular: ['parent', 'angle', 'distance'],
pivotX: 0.1,
pivotY: 0.1,
translationX: 225,
rotationZ: 'rotation'
}
}
}
This (above) will generate a collection constraints for each Composable created with the tag 'box'. The will have circular constraints (typically circular:[viewid, angle, distance]) but the angle will be different for each widget created. The first starting at 'startRotation' (which is undefined at this point) When constructing the ConstraintSet overridVariables would have to contain startRotation:
var cs1 = ConstraintSet(baseConstraintSet, overrideVariables = "{ startRotation: 0 }")
var cs2 = ConstraintSet(baseConstraintSet, overrideVariables = "{ startRotation: 90 }")
Constructing the ConstraintSet without oveeridVariables will result in and exception
The pattern is typically
setId1:{ // setId1 is the name of the ConstraintSet equivalent to <ConstraintSet android:id="@id/setId1" ...>
widgetId1{ ...} // widgetId1 is the id of the widget equivalent of <Constraint android:id="@id/..." ...>
widgetId2{ ...}
widgetId3{ ...}
}
setId12:{
widgetId1{ ...}
widgetId2{ ...}
widgetId3{ ...}
}
}
Example:
ConstraintSets: {
start: {
a: {
width: 40,
height: 40,
start: ['parent', 'start', 16],
bottom: ['parent', 'bottom', 16]
}
},
end: {
a: {
width: 40,
height: 40,
end: ['parent', 'end', 16],
top: ['parent', 'top', 16]
}
}
}
Support the following key words
- width
- height
- start
- end
- top
- bottom
- left
- right
- center
- centerHorizontally
- centerVertically
- alpha
- scaleX
- scaleY
- translationX
- translationY
- translationZ
- pivotX
- pivotY
- rotationX
- rotationY
- rotationZ
- visibility (= visible, invisible, gone)
- custom - custom properties
- motion { ... } - motion properties
Within a Constraint section you can have a MotionSection The MotionSection of the starting ConstraintSet modifies a Transitions behavior
motion: {
pathArc : 'startHorizontal',
stagger: 3,
quantize: [6, 'overshoot', 32]
easing: 'overshoot'
}
This has the Motion of the Composable move in a quarter ellipse It has 4 possible values:
- none - travel in default monotonic spline
- startHorizontal - indicate the it starts going horizontal arching till it ends vertical
- startVertical - indicate the it starts going vertical arching till it ends horizontal
- flip - indicate the it starts going vertical and alternate between horizontal and vertical at each KeyPosition
The stagger sets the order of this Composable in the staggered moves.
To use this you add "staggered" to the transition where 0.0 means not staggering and 1.0 means 1 at a time.
Transitions: {
default: {
staggered: 0.4
}
}
Quantize cause the transition of this composable to happen in steps.
quantize: 10
means it will make 12 jumps in motion at progress 0.0 then 0.1, 0.2, etc. till it progress hits 1.0.
(Like a watch ticking)
Quantize can take a list of 3 values quantize: [steps, easing, phase]
- steps is the number of jumps
- easing is the shape of the jump (See easing for all options on easing)
- phase is when to start the jumps within that 1/steps sections of the progress
for example quantize: [6, 'overshoot', 0]
The jumps can be designed to be non discreet yet quantized. (like click stops on a dial)
the above will move in 6 steps overshooting and bouncing back.
The 0 is the phase of when the step occurs (almost always = 0)
Easing specifies the rate of change of progress. This takes about 3 different forms
- named easing curves:
easeInOut
,easeIn
,easeOut
,linear
,bounce
,overshoot
oranticipate
- cubic like you see in css :
cubic(0.34, 1.56, 0.64, 1)
- evenly spaced monotonic spline starting at 0 ending at 1 :
spline(0,1.2,1)
,spline(0, 0.05, 0.1, 0.8, 0.9, 1)
For compose custom properties must be extracted and set manually. So for a typical custom property set:
custom: {
background: '#0000FF',
textColor: '#FFFFFF',
textSize: 12
}
You would need to set them using the below logic.
var properties = motionProperties("a")
Text(text = "Hello", modifier = Modifier
.layoutId(properties.value.id())
.background(properties.value.color("background"))
,color = properties.value.color("textColor")
,fontSize = properties.value.fontSize("textSize")
)
Transitions is a container for all transitions. Each Transition is given a name. The name "default" is special and defines the initial transition.
Transitions: {
default: {
from: 'start',
to: 'end',
pathMotionArc: 'startHorizontal',
KeyFrames: {
KeyPositions: [
{
target: ['a'],
frames: [25, 50, 75],
percentX: [0.4, 0.8, 0.1],
percentY: [0.4, 0.8, 0.3]
}
],
KeyCycles: [
{
target: ['a'],
frames: [0, 50, 100],
period: [0 , 2 , 0],
rotationX: [0, 45, 0],
rotationY: [0, 45, 0],
}
]
}
}
The above is a transition from ConstraintSet "start" to ConstraintSet "end" paths with move in an Arc (quarter ellipse) starting horizontally. It also contains Keframes of type keyCycle and KeyPosition.
We currently only support 3 types of keyFrames. All types of key frames will have the following:
- target - id's of the key widgets that these key positions apply
- frames - The position on the frames of each of the keys
For people familiar with MotionLayout in 2.x this is a more compact expression. For each target, for each frame a keyFrame is generated. i.e.
target: ['a', 'b'],
frames: [0, 50, 100],
// will generate 6 key frames
- KeyPosition - Manipulate the path of the view. KeyPosition Video
- KeyAttributes - Miniplate a view Transform and custom attributes. KeyAttributes Video
- KeyCycle - Produce repeating patterns.KeyCycle Video
- percentWidth
- percentHeight
- sizePercent
- percentX
- percentY
- frame - The widgets to transform
- target - the key frames
- curveFit - interpolate between points using linear interpolation or a monotonic spline
- alpha - how transparent the widget should be.
- rotationZ - post layout rotate the widget
- rotationX - post layout rotate about a horizontal axis
- rotationY - post layout rotate about a vertical axis
- pivotX - post layout the point around which to rotate
- pivotY - post layout the point around which to rotate
- pathRotate - The angle with respect to the path of the widget to rotate
- scaleX - post layout
- scaleY - post layout
- translationX - post layout move the widget to the right
- translationY - post layout move widget down
- translationZ - post layout move widget towards the viewer expanding its shadow
(Not implemented yet pivotTarget, easing, progress )
- offset - the center of the wave
- phase - the phase angle of the wave in this area
- period - the number of waves to generate in this area
- frame - The widgets to transform
- target - the key frames
- curveFit - interpolate between points using linear interpolation or a monotonic spline
- alpha - how transparent the widget should be.
- rotationZ - post layout rotate the widget
- rotationX - post layout rotate about a horizontal axis
- rotationY - post layout rotate about a vertical axis
- pivotX - post layout the point around which to rotate
- pivotY - post layout the point around which to rotate
- pathRotate - The angle with respect to the path of the widget to rotate
- scaleX - post layout
- scaleY - post layout
- translationX - post layout move the widget to the right
- translationY - post layout move widget down
- translationZ - post layout move widget towards the viewer expanding its shadow
(Not implemented yet pivotTarget, easing, progress ,waveShape )