Animate is an experimental animation dialect. It's main goal is to provide the programmers with an easy declarative way to describe simple animation as an extension to Draw. It also provides mechanism for animating arbitrary word! or path! values.
At the heart of Animate is the process known as Inbetweening - that is generating intermediate frames between two keyframes. Animate uses a function called tween
for this. It's syntax is as follows:
tween <va11> <val2> <t> <ease>
<val1> : Value to interpolate from (number! pair! tuple!)
<val2> : Value to interpolate to (number! pair! tuple!)
<t> : a value between 0.0 and 1.0 (float!)
<ease> : Easing function (function!)
tween
uses the indicated easing function to interpolate a value between val1
and val2
at t
- a floating point number between 0.0 and 1.0. Using explicit calls to tween
you can animate anything in Red, including GUI controls.
There are several predefined easing functions:
- ease-linear
- ease-in-sine
- ease-out-sine
- ease-in-out-sine
- ease-in-out-power
- ease-in-quad
- ease-out-quad
- ease-in-out-quad
- ease-in-cubic
- ease-out-cubic
- ease-in-out-cubic
- ease-in-quart
- ease-out-quart
- ease-in-out-quart
- ease-in-quint
- ease-out-quint
- ease-in-out-quint
- ease-in-expo
- ease-out-expo
- ease-in-out-expo
- ease-in-circ
- ease-out-circ
- ease-in-out-circ
- ease-in-back
- ease-out-back
- ease-in-out-back
- ease-in-elastic
- ease-out-elastic
- ease-in-out-elastic
- ease-in-bounce
- ease-out-bounce
- ease-in-out-bounce
- ease-steps
Screnshot taken from easings.net
The main goal of Animate is to extend Draw in the time domain. This is done by using "augmented" draw block. Every block of Draw commands is a valid animation block. In order to animate the various Draw primitives, Animate introduces new keywords. Before we present them, let's see how to connect animate
to Draw.
anim-block: [
; Draw and Animate commands
]
view [
canvas: base 600x400 rate 67
draw animate anim-block
]
The animate
function parses a block of draw and animation commands, prepares all the tweens for the animation and returns a Draw block.
animate <commands>
<commands> : a block of Draw and animate commands (block!)
animate
uses a global event handler to process on-time
events and that's why the rate facet should be set for at least one face for animation to work.
Before we start to animate the parameters of Draw commands, we need to indicate the reference frames.
start <start> <duration> <delay> <ease> <loop>
<start> : starting time (<anim-start>)
<duration : (optional) duration of the animation (time! float! integer!). Default value 1.0
<delay> : (optional) delay between successive subanimations (time! float! integer!). Default value 0.0
<ease> : (optional) easing function (lit-word! function!). Default value 'ease-linear
<loop> : (optional) how does the animation repeat (<anim-loop>). Default - no repetition
<actors> : (optional) actors for the animation events (<anim-actors>)
<anim-start> : <start-n> | <adverb> <ref> <end>
<start-n> : (optional) time in seconds (time! float! integer!)
<adverb> : relation to the reference animation (word!)
<ref> : reference animation already declared (word!)
<end> : which side (word!)
<adverb> is one of the following:
- when
- after
- before
<end> is one of:
- starts
- ends
anim-loop : loop <two-way> <count>
<two-way> : (optional) 'two-way (the animation repeats backwards)
<count> : (optional) 'forever | <n> 'times
<n> : how many times does the animatin repeat (integer!)
ref: start 2.0 duration 5.0
start 1.0 before ref starts duration 2.0
start when ref starts duration 3.0 ease :ease-in-out-cubic
start 2.0 after ref starts duration 5.0
start 2.0 before ref ends duration 2.0 delay 1.0 ease :ease-in-out-cubic loop two-way
start when ref ends duration 5.0 loop two-way forever
start 2.0 after ref ends ease :ease-in-elastic loop 3 times
The values set by start
are used until the next start
construct. A set-word! can precede start
and thus the following animation frames can refer to it.
The start
declaration does not animate anything by itself. In order to animate a value, one needs to use from value1 to value2
syntax:
from to
<id> : (optional) identifier – only needed for pausing and resuming (set-word!)
<value> : start or end value to be animated (number! pair! tuple!)
<speed> : (optional) animation speed (<anim-speed>)
<actors> : (optional) <event> block!
Actors specify blocks of code thata is evaluated at specific animation events. Events are:
on-start : once, at the animation start
on-exit : once, when the animation ends
on-time : each time the animation is updated.
anim-speed : 'speed <speed-value>
<speed-value> : the speed value (number!)
on-time
is triggered at the rate specified by the face to which the draw block is attached. time
word can be used within the block following on-time
- it holds the time elapsed from the animation start.
All numeric values in an animation block can be animated using from
- integers, floats, pairs and tuples (for colors).
line-width from 1 to 10
pen from black to red
translate from 0x0 to 100x25 on-start [started: true]
scale from 0.0 to 1.0 1.0 on-time [label/data: time]
box 10x10 from 10x10 to 200x50 one-exit [print "Finished!"]
Each from
construct is translated at the parse time to a call to tween
function.
If delay
in a start
reference frame is non-zero, each animation defined by from
starts the amount of delay
later than the previous. That is why the actors are defined at from
level and not at the start
level. start
could define a reference frame that starts at 1.0 with duration 5.0 and delay 1.0. If there are 3 values animated with from
, the first one will start at 1.0, the second - at 2.0 and the third will start at 3.0 and will finish at 8.0. With actors defined at from
level one can monitor each effect independently. That wouldn't have been possible if actors were used at start
level.
Next start
resets the delay.
Animate
introduces several effects that are meant to automate some of the more frequently used tasks in animations.
particles
animates a simple particle system:
particles <id> <prototype> <expires> <actors>
<id> : effect identifier (word!)
<prototype> : a block describing the particles (block! word!)
<expires> : (optional) when to clear the effect's draw black
<speed> : (optional) animation playing speed (‘speed number!)
<actors> : (optional) actors for the events
Prototype is a block of set-word! and value pairs that is used to create each individual partilce as well as to set up the entire effect:
number: <integer!>
emitter: <function!>
absorber: <function!>
shapes: <block!>
forces: <block!>
ffd: <integer! float!>
Most of the above parameters are optional. If some is not present, the default value is obtained from the prototype object:
particle-base: make object! [
number: 100 ; how many particles
start: 1.0 ; start time of the effect
duration: 5.0 ; duration of the effect
elapsed: 0.0
shapes: speck ; a block of draw blocks (shapes to be used to render particles)
ffd: 0 ; how many seconds of the animation to be skipped
forces: [] ; what forces affect the particles motion - a block of words
started: false
finished: false
expires: 0 ; when to clear the particle draw block
paused: false
bi-dir: false
on-start: []
on-time: []
on-exit: []
]
Only emitter
and absorber
are mandatory, since they define how the particles are generated and when they cease to exist and need to be respawned.
emitter
is a function with no arguments that returns a block that holds the parameters of the particle to be generated. These parameters are x
and y
coordinates (integer! float!) where the particle starts its life cycle, dir
- direction in which it moves (integer! float!), speed
- its speed (integer! float!). color
(tuple! word!) can be used for fade-in and fade-out effects, but only if the description of the particle (given by shapes
) does not include fill-pen
, since it will overwrite this color. There is a time parameter t
that holds the elapsed time for the particle. It can be used by absorber
function to decide if its time
So, the emitter
function takes no arguments, generates some values and returns a block of named values for some or any of x y dir speed color t
parameters:
compose [x: (x) y: (y) dir: (d) speed: (s) t: (random 8.0)]
All the individual particle's parameters and their default values are listed below:
a-particle: context [
x: 0.0 ; X position
y: 0.0 ; Y position
dir: 45.0 ; movement direction in degrees
speed: 1.0 ; speed
scale-x: 1.0 ; X scale factor
scale-y: 1.0 ; Y scale factor
color: transparent ; fill-pen color
t: 0.0 ; elapsed time for the particle
shape: [] ; drawing commands
data: none
]
At each frame the position of each particle is updated automatically using the dir
and speed
parameters. dir
and speed
(as well as any other parameter) can be influenced by any functions in the forces
block. Each force- function should receive a single parameter - a block of current particle's parameters and should return the block after modification.
Example force- function
wind: function [p][
if p/dir < 135 [p/dir: 135 + p/dir / 2.0]
p/dir: p/dir + 2.0 - random 4.0
p
]
The updated particle's coordinates are tested with the absorber
function with particles's block as a single argument. If the function returns true
, the particle's position is reset by invoking the emitter
function.
Examples for absorber
absorber: function [
{Returns true if the particle needs to be re-emitted}
p
][
to-logic any [p/x < 50 p/x > 150 p/y < 50 p/y > 350]
]
Since a particle effect usually displays hundreds and thousands of objects, it's to free the drawing resources when the effect finishes. expires
and optional and if not present, the last drawn frame is kept in draw block.
expire after <time>
<time> : how many seconds after the beginning of the effect to clear the effect's draw commands (integer! float!)
The starting time of particles
is taken from the last start
(and subsequent from
, if delay
is non-zero). duration
and ease
- too. Looping doesn't make much sense for particle effects and that's why is not supported.
particles
and all other effects in animate
support the same actors as from
.
curve-fx
flows a text or one or several draw blocks along a Bézier curve. Standard Bézier curves in Draw support 3 or 4 points. curve-fx
supports up tp 30 points (not-thoroughly tested!).
curve-fx <id> <effect> <value> <expires> <actors>
<id> : effect identifier (word!)
<effect> : effect data (block! word!)
<value> : position on the curve (float! (from float! to float!))
<expires> : (optional) when to clear the effects draw block (same as particles)
<speed> : (optional) animation playing speed (‘speed number!)
<actors> : (optional) on event actors (same as from)
Effect data is a block with set-word! and value pairs:
data: <string! block!> (text or block of draw blocks)
font: <object! word> (optional - if data is a string)
curve: <block!> (a block of pairs - points of the Bézier curve)
space-x: <float! integer!> (character spacing for strings - float!, or horizontal offset between blocks)
fnt1: make font! [name: "Verdana" size: 16 color: black]
red-text: "Red is a next-gen programming language, strongly inspired by REBOL"
bez-pts: [50x40 370x-120 350x100 250x400 620x200]
red-info: [data: red-text font: fnt1 curve: bez-pts space-x: 0.98]
block: [
font fnt1
start 1.0 duration 2.0 delay 1.0 ease :ease-in-out-cubic
curve-fx Red-lang red-info from 1.0 to 0.0 expires after 5
]
If the effect's data is block of draw blocks, the blocks are translated and oriented on the curve one after another starting at the current position and adding the offset along the curve.
text-fx
allows to easily animate text element's parameters: color, scaling and position. It can preocess the provided string in three modes: lines, words and characters.
text-fx <fx-block> <parameters> <expires> <actors>
<fx-block> : a block defining the text effect (block!)
<parameters> : (optional) effect parameter name and values (params)
<expires> : (optional) when to clear the effects draw block (same as particles)
<actors> : (optional) on event actors (same as from)
fx-block
is a block containing the data neded for creation and animation of the text. Here are the default values. One need to specifiy the id, text and font, the others can be obtained by the default values:
id: none ; identfier
text: "" ; text to render
font: none ; font to use
mode: 'chars ; how to split the text
posXY: 0x0 ; where to place the text
sp-x: 1.0 ; spacing factor for X direction
sp-y: 1.0 ; spacing factor for Y direction
from: 'center ; origin of scaling
random: off ; process the text element not in order, but randomly
mode is one of 'lines, 'words or 'chars
params
are:
text-scale <scale-value> <scale-value>
text-move <move-value>
text-color <color-value>
scale-value : float! or (from float! to float!)
move-value : pair! or (from pair! to pair!)
color-value : color-v or (from color-v to color-v)
color-v: tuple! or word!
fnt: make font! [name: "Brush Script MT" size: 28 color: 25.12.5.255]
text: {Red’s ambitious goal is to build the world’s
first full-stack language, a language you can
use from system programming tasks,
up to high-level scripting through DSL.}
txt-bl: compose [
id: 'test text: (text) font: (fnt)
posXY: 20x20 sp-y: 0.75
mode: 'chars from: 'center random: off
]
anim-block: compose [
fill-pen (papaya + 0.20.30)
box 0x0 720x200
st: start 2.0 duration 0.3 delay 0.02 ease :ease-in-out-cubic
text-fx txt-bl text-scale from 4.0 to 1.0 from 4.0 to 1.0
text-color from 25.12.5.255 to 25.12.5.0
]
In the above example the multiline string is displayed character after character (mode: 'chars). Both character scaling and color are animated at the same time. Characters start big (4 times bigger) and transparent and at the end are their normal size and dark color. The time offset is controlled by delay
.
stroke-path
traces a block of drawing primitives and gradually displays the already traced part with a given color and line width.
stroke-path <id> <path> width <width> color <color> <expires> <actors>
<id> : effect id (word!)
<path> : a block of lines, arcs and custom Bézier curves (block!)
<width> : line width (integer!)
<color> : line color (tuple! word!)
<expires> : (optional) when to clear the effects draw block (same as particles)
<speed> : (optional) animation playing speed (‘speed number!)
<actors> : (optional) on event actors (same as from)
Path contents are limited to just three types of primitives: lines, circular arcs and custom (multipoint) Bézier curves. The path is not closed automatically. Please note that only the x
radius of an arc is used (no elliptical arcs)
path: [
line 100x100 220x100 180x200 250x200
arc 250x250 50x50 270 90
bezier 300x250 320x400 450x200 500x300
]
path-block: compose [
line-cap round
start 1 duration 2 ease :ease-in-out-quad
stroke-path test (path) width 15 color red expires after 4
on-start [print "Starting path1"]
]
morph-path
takes a path and morphs it into another path. The paths are the same as in stroke-path
.
morph-path <id> <path> into <path> <visible> <expires> <actors>
<id> : an identifier – used to pause and resume the effect (word!)
<path> : a block of lines, arcs and custom Bézier curves (block!)
<visible> : (optional) Is the first block displayed at the start? <visible <logic!>>
<expires> : (optional) when to clear the effects draw block (same as particles)
<speed> : (optional) animation playing speed (‘speed number!)
<actors> : (optional) on event actors (same as from)
path1: [
arc 200x200 180x180 180 180
arc 200x200 180x180 0 180
]
path2: [
line 100x100 250x100 arc 250x150 50x50 270 90 line 300x150 300x250
bezier 300x250 250x350 200x250 150x200 100x250
line 100x250 100x100
]
morph-path-block: compose/deep [
pen red
line-width 3
line-cap round
start 2 duration 2 ease :ease-in-out-quint
morph-path morph (path1) into (path2) visible true expires after 2
]
morph-path
uses the current pen and line-width settings.
For best results it's a good idea to match the starting point of the path as close as possible. The number of points of the paths doesn't matter.
When you need to play an animation within a start
reference frame with a different speed, you can use the speed
parameter. It is supported by both simple animations (from
) and all effects with the exception of text-fx
translate white-box: from 30x95 to 550x95 speed 2.5 [box 0x0 20x20]
particles fleet rocket expires after 6 speed 2
curve-fx info red-info from 1.0 to 0.0 expires after 6 speed 125%
toggle-animation
function can be used to pause or resume an animation. It needs only one argument - a lit-word!
identifying the animation. It doesn't currently work for text-fx
effect, because it uses a different approach.
toggle-animation 'vulcano
Please note that toggle-animation
is usually used in your event-processing code.
There are many things that can be added to the animation system:
- stop
- rewind
- ffd
Now it works on all components, depending on the mode - lines, words or characters. It would be good to specify which ones to animate. Possible parameters: index, range, or block of indices.
An effect that changes the content of an image frame by frame
- rectangular
- polar