Give the children of a group their own clock, run them faster with the group speed, and start them with the group.
A composition group runs its children on its own clock, like a precomposition in a motion design tool. The clock starts when the group starts on the page, and the group’s speed runs it faster or slower. The editor has no composition control, so you set it with the block API.
This guide covers how to turn composition on, how to run the children faster, how to start them with the group, what a plain group does, and the error to expect. It ends with a frame export.
Creating the Lanes#
Each lane of the example is a group with a gray line and a ball on page, the 6-second page of the scene. The ball has a position/x track and crosses the page in 4 seconds of its own time. group groups the two blocks.
// Creates a lane: a group with a line and a ball that crosses the page in// 4 seconds of its own timefun createLane( y: Float, color: RGBAColor,): DesignBlock { val line = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block = line, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setFill(block = line, fill = engine.block.createFill(FillType.Color)) engine.block.setFillSolidColor(block = line, color = Color.fromRGBA(r = 0.85F, g = 0.85F, b = 0.85F, a = 1F)) engine.block.setWidth(block = line, value = 1680F) engine.block.setHeight(block = line, value = 6F) engine.block.setPositionX(block = line, value = 120F) engine.block.setPositionY(block = line, value = y + 37F) engine.block.appendChild(parent = page, child = line) // Long enough for every lane clock, so the line stays visible engine.block.setDuration(block = line, duration = 12.0)
val ball = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block = ball, shape = engine.block.createShape(ShapeType.Ellipse)) engine.block.setFill(block = ball, fill = engine.block.createFill(FillType.Color)) engine.block.setFillSolidColor(block = ball, color = color) engine.block.setWidth(block = ball, value = 80F) engine.block.setHeight(block = ball, value = 80F) engine.block.setPositionY(block = ball, value = y) engine.block.appendChild(parent = page, child = ball) engine.block.setDuration(block = ball, duration = 4.0) engine.block.setKeyframes( block = ball, property = "position/x", keyframes = listOf( Keyframe(time = 0F, value = KeyframeValue.Float(120F)), Keyframe(time = 1F, value = KeyframeValue.Float(1720F)), ), )
val lane = engine.block.group(blocks = listOf(line, ball)) engine.block.setDuration(block = lane, duration = 6.0) return lane}Turning Composition On#
group/composition is a bool property of a group and is off by default. setBoolean turns it on, and getBoolean reads it. The children of the first lane now run on the group’s clock, which starts when the group starts on the page.
// The first lane is a composition group. Its children run on the group's// own clock, which starts when the group starts on the page.val normal = createLane(y = 150F, color = Color.fromRGBA(r = 0.6F, g = 0.6F, b = 0.6F, a = 1F))engine.block.setBoolean(block = normal, property = "group/composition", value = true)// trueval normalIsComposition = engine.block.getBoolean(block = normal, property = "group/composition")Running the Children Faster#
setPlaybackSpeed on a composition group runs its children that many times faster. The speed of a group has no upper bound, and getPlaybackSpeed reads it back. At speed 2, the blue ball crosses the page in 2 seconds. Its duration of 4 seconds is measured on the composition clock, so the ball disappears after 2 seconds of the page.
// At speed 2, the children see 2 seconds for every second of the pageval fast = createLane(y = 400F, color = Color.fromRGBA(r = 0.2F, g = 0.4F, b = 0.9F, a = 1F))engine.block.setBoolean(block = fast, property = "group/composition", value = true)engine.block.setPlaybackSpeed(block = fast, speed = 2F)// 2.0val fastSpeed = engine.block.getPlaybackSpeed(block = fast)Starting the Children With the Group#
The time offsets of the children are measured on the composition clock, which starts with the group. setTimeOffset starts the third group 1 second into the page, so its ball starts 1 second later than the others.
// The group starts 1 second into the page, and its children start with itval late = createLane(y = 650F, color = Color.fromRGBA(r = 0.9F, g = 0.3F, b = 0.4F, a = 1F))engine.block.setBoolean(block = late, property = "group/composition", value = true)engine.block.setTimeOffset(block = late, offset = 1.0)engine.block.setDuration(block = late, duration = 5.0)A Plain Group#
A group without composition keeps its children on the page clock and ignores its speed. The green ball crosses the page at the same pace as the gray one, although its group has speed 2.
// Without composition, the speed of a group has no effectval plain = createLane(y = 900F, color = Color.fromRGBA(r = 0.2F, g = 0.7F, b = 0.3F, a = 1F))engine.block.setPlaybackSpeed(block = plain, speed = 2F)// falseval plainIsComposition = engine.block.getBoolean(block = plain, property = "group/composition")Log.i(TAG, "$normalIsComposition, $fastSpeed, $plainIsComposition")Handling Errors#
A group runs at 0.25 times the page speed or faster. A lower speed throws an EngineException with a code, and the speed stays unchanged.
val errorCode = try { // A group runs at 0.25 times the page speed or faster engine.block.setPlaybackSpeed(block = normal, speed = 0.1F) null} catch (error: EngineException) { // EDITOR.PLAYBACK_SPEED_TOO_LOW error.code}Exporting a Frame#
We export the page as PNG at 1.5 seconds. The gray and green balls are at 37.5 percent of their lanes, the blue ball at 75 percent, and the red ball at 12.5 percent, because its group started 1 second later.
// Export the frame at 1.5 secondsengine.block.setPlaybackTime(block = page, time = 1.5)val previewPng = engine.block.export(block = page, mimeType = MimeType.PNG)Log.i(TAG, "PNG bytes: ${previewPng.remaining()}")Composition Groups and Other Block Operations#
- Nested composition groups multiply their speeds.
- A child’s duration is not clamped to the group duration. A child is hidden after its end on the composition clock.
playback/timeRemapwithplayback/timeRemapEnabledreplaces the group’s local time with a value or a keyframe track, and the speed still multiplies it.- Ungrouping a composition group converts the time offset and duration of each child to the page clock, so the children keep playing at the same moments.
- Saving and loading keep
group/compositionand the speed, and undo and redo restore them. - The HTML5 export does not export the composition clock, so the children play at page time there.
Troubleshooting#
- The speed is too low (
EDITOR.PLAYBACK_SPEED_TOO_LOW): use a speed of 0.25 or more. - The speed is not a number (
BLOCK.VALUE_NOT_FINITE): pass a finite speed. - The speed has no effect: turn on
group/compositionfor the group. - A child disappears early: its duration is measured on the composition clock, so a faster group ends it sooner on the page. Make the child longer.
API Reference#
| Method | Purpose |
|---|---|
engine.block.create(blockType=_) |
Create a graphic block |
engine.block.createShape(type=_) |
Create a rect or ellipse shape |
engine.block.setShape(block=_, shape=_) |
Give a graphic its shape |
engine.block.createFill(fillType=_) |
Create a color fill |
engine.block.setFill(block=_, fill=_) |
Give a graphic its fill |
engine.block.setFillSolidColor(block=_, color=_) |
Set the fill color |
engine.block.setWidth(block=_, value=_) |
Set the width of a block |
engine.block.setHeight(block=_, value=_) |
Set the height of a block |
engine.block.setPositionX(block=_, value=_) |
Set the x position of a block |
engine.block.setPositionY(block=_, value=_) |
Set the y position of a block |
engine.block.appendChild(parent=_, child=_) |
Add a block to the page |
engine.block.setDuration(block=_, duration=_) |
Set the duration of a block or group |
engine.block.setKeyframes(block=_, property=_, keyframes=_) |
Move the ball |
engine.block.group(blocks=_) |
Group blocks into a group |
engine.block.setBoolean(block=_, property="group/composition", value=_) |
Turn composition on or off |
engine.block.getBoolean(block=_, property="group/composition") |
Check whether a group is a composition group |
engine.block.setPlaybackSpeed(block=_, speed=_) |
Set the speed of a group, 0.25 or more |
engine.block.getPlaybackSpeed(block=_) |
Read the speed of a group |
engine.block.setTimeOffset(block=_, offset=_) |
Start a group later on the page |
engine.block.setPlaybackTime(block=_, time=_) |
Seek before you export a frame |
engine.block.export(block=_, mimeType=_) |
Export the frame as PNG |
Next Steps#
- Keyframes: animate the children of a composition group.
- Create Animations: add preset animations to blocks.