Skip to content

Commit af62a11

Browse files
Animation part 3, timelines
1 parent 72fec3d commit af62a11

File tree

3 files changed

+76
-9
lines changed

3 files changed

+76
-9
lines changed

guides/animation/part-2-signals/src/AnimationPart2.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,11 @@ object AnimationPart2 extends IndigoSandbox[Unit, Unit]:
173173
/** ## Summary
174174
*
175175
* Using signals and signal functions provides a great abstraction for animation, much better than
176-
* coding it up directly. Could get pretty messy if you were trying to do a complicated animation
177-
* though, don't you think? What if you were trying to coordinate several moving elements or
178-
* effects at once, such as in a menu or game over screen?
176+
* coding it up directly. Perfect for simple animations.
177+
*
178+
* That said, things could get pretty messy if you were trying to do a complicated animation. What
179+
* if you were trying to coordinate several moving elements or effects at once, such as in a menu
180+
* or game over screen?
179181
*
180182
* What we need an abstraction for our abstraction, and luckily, one is available. In the next
181183
* section, we'll look at timelines.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,19 @@
11
# Part 3: Timeline Animations
2+
3+
In the previous part of the animations series, we looked at encoding our animations as signals and signal functions, and concluded by identifying the need for an even higher level of abstraction for when things get really complicated.
4+
5+
Timelines allow you to describe complicated animations, with potentially many layers and things going on over time. They are essentially made up of `SignalFunction`s that are wrapped up in a nice DSL.
6+
7+
## Timeline basics
8+
9+
Each timeline can animate one type of thing, but they and their sub components are reusable and composable.
10+
11+
Timeline animations are build up of 'layers' (terminology clash alert: animation layers, not visible layers) which each animate one property. If you wanted to add another animated value then you would add another layer. The two layers would then be squashed together automatically to produce the end result.
12+
13+
Timeline layers are each their own sequence of 'time slots', such as `startAfter` and `animate`. Time slots form a back-to-back chain of things to do. In the example below, there are two slots in use, but you can have as many as you like.
14+
15+
## Animate everything!
16+
17+
Timelines do now know that they are for 'animation', and can 'animate' anything at all. We just need to change our idea of animation from something like 'moving a picture' to 'producing a value over time following a series of transformations'.
18+
19+
For example, another interesting use of animations is to cross-fade music by animating the volume of two tracks.

guides/animation/part-3-timelines/src/AnimationPart3.scala

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package indigoexamples
22

33
import indigo.*
4+
import indigo.syntax.*
5+
import indigo.syntax.animations.*
46
import generated.Config
57
import generated.Assets
68

@@ -28,14 +30,59 @@ object AnimationPart3 extends IndigoSandbox[Unit, Unit]:
2830
def updateModel(context: Context[Unit], model: Unit): GlobalEvent => Outcome[Unit] =
2931
_ => Outcome(model)
3032

33+
val circle =
34+
Shape.Circle(
35+
Circle(Point.zero, 25),
36+
Fill.Color(RGBA.Red),
37+
Stroke(2, RGBA.White)
38+
)
39+
40+
/** ## How to make a timeline animation
41+
*
42+
* In this animation, we have two layers.
43+
*
44+
* The first layer initially waits 2 seconds. Then over the next 5 seconds, it calculates a
45+
* points position diagonally (lerp means linear interpolation) from one corner of the viewport
46+
* to the other, and finally moves a circle to that position. All of this is performed using an
47+
* 'ease-in-out' function that accelerates the movement up initially and slows it down towards
48+
* the end.
49+
*
50+
* The second layer also waits 2 seconds for consistency, then fades the circles fill color in,
51+
* over time.
52+
*
53+
* The function inside the `animate` block is built up using `SignalFunction`s (see below) to
54+
* describe the value transformation that results in the animated movement. There are lots of
55+
* helpful signal functions available on the `SignalFunction` companion object for you to make
56+
* use of.
57+
*/
58+
// ```scala
59+
def myTimelineAnimation(viewportSize: Size): Timeline[Shape.Circle] =
60+
timeline(
61+
layer(
62+
startAfter(2.seconds),
63+
animate(10.seconds) { circle =>
64+
easeInOut >>>
65+
lerp(Point(60), viewportSize.toPoint - Point(60)) >>>
66+
SignalFunction(pt => circle.moveTo(pt))
67+
}
68+
),
69+
layer(
70+
startAfter(2.seconds),
71+
animate(10.seconds) { circle =>
72+
lerp >>>
73+
SignalFunction { alpha =>
74+
circle.withFill(Fill.Color(RGBA.Green.withAlpha(alpha)))
75+
}
76+
}
77+
)
78+
)
79+
3180
def present(context: Context[Unit], model: Unit): Outcome[SceneUpdateFragment] =
3281
Outcome(
3382
SceneUpdateFragment(
34-
Shape
35-
.Circle(
36-
Circle(Point.zero, 50),
37-
Fill.Color(RGBA.Red),
38-
Stroke(2, RGBA.White)
39-
)
83+
myTimelineAnimation(context.frame.viewport.bounds.size)
84+
.atOrLast(context.frame.time.running)(circle)
85+
.toBatch
4086
)
4187
)
88+
// ```

0 commit comments

Comments
 (0)