How to animate display widgets
One of the features of MPF is that you can animate display widgets. Animating a widget means that you can change a widget's properties over time. You can pretty much change any numeric property, including size, position, opacity, etc.
When animating widgets, you specify multiple properties to change at the same time, or a sequence of changes one after the other (or both). You can also specify the duration of each step, the "easing" formula that affects the curve (acceleration/deceleration) of the change, and whether the animation is a one-time thing or a repeating loop.
You can also configure animations to start playing as soon as the widget is created, or tie steps (or series of steps) to MPF events, meaning a widget might be static, then the event "move_widget" is posted and it moves, then the event "remove_widget" is posted and it's animated away.
This How To guide will show you how to do all of that.
1. Understanding animations in MPF
MPF animations are properties of widgets. For example, here's a basic widget with no animations:
slides: slide_1: widgets: - type: text text: MY TEXT color: red
To add animations to a widget, you simply add an
to that widget, and then under there you add specific animation steps
and settings. For example:
slides: slide_1: widgets: - type: text text: MY TEXT color: red animations: show_slide: # animation trigger event - property: opacity # name of the widget property we're animating value: 1 # target value of that property for this step duration: .5s # duration for this step (how long it takes to get there) - property: opacity # second step in the animation (starts with a hyphen) value: 0 duration: .5s repeat: true # added to the final step, tells this animation to repeat (loop)
In the example above, an
animations: setting has been added to the
widget. Then under there, you add the name of the event you want to use
to trigger this animation to start. In this case, we use a special event
show_slide: which means these animations are triggered when the
slide is shown on a display.
Next, notice that under the event, there are two steps (each beginning with a hyphen and a space).
There are several settings you can specify in each step. (See the config file reference for animations for details)
In this example, there are three settings for the first step:
- property: opacity value: 1 duration: .5s
The property setting is the name of the widget's property that you
want to animate. This can be almost any numerical property of the
opacity, etc. (Different widget types
have different types of animatable properties. For example, on text
widgets you can animate the
font_size:, on various shape widgets you
can animate the
2. Relative animation values
Sometimes it is desirable to animate a value a relative amount from a
widget's current value rather than specifying an absolute target value.
This can be done using
relative: True. With the
True, the new target value will set by adding the
parameter to the widget's current
property: value when the animation
relative: is set to
False, the animation target uses
value: property value as its destination.
The following example animates a widget 50 pixels in the
x direction over one second from its current location,
: then -50 pixels in the
y direction over another second followed by
a 45 degree rotation over 500 ms:
3. Animation trigger events
The animation trigger event (which is the
show_slide: entry in the
example from the previous step is the name of the MPF event you want to
use to start the animation.
In most cases, however, you'll probably want to trigger an animation to start playing when the slide is created, so in addition to being able to use any MPF event, there are also a few special events (sometimes called "magic events") that have special meaning here:
This event is triggered when a widget is added to a slide. This is useful when you're using the widget_player to add to new widget to an existing slide, and you want an animation to be applied to that widget as soon as it's added.
This event is triggered when a widget is is removed from a slide.
This event is triggered when the slide this widget is part of is about
to be shown. This doesn't necessarily get called when the slide is
created or when the
slide_player: event happens, because if the slide
is not the highest priority slide, then the slide will be created but
not shown. So this event happens right before the slide is shown.
If there's an entrance transition, this method is called BEFORE the transition starts. In other words, it means the animation will be playing as the slide transition is happening.
This event is triggered when the slide this widget is part of has been
shown and is the current slide on the display. This doesn't necessarily
get called when the slide is created or when the
happens, because if the slide is not the highest priority slide, then
the slide will be created but not shown. So this event happens right
before the slide is shown.
If there's an entrance transition, this method is called AFTER the transition starts. In other words, it means the animation will NOT be playing as the slide transition is happening.
This event is triggered by the current slide that's being shown on a display is about to be replaced by another slide.
If there's an exit transition, this method is called BEFORE the transition starts. In other words, it means the animation will be playing as the slide transition is happening.
This event is triggered by the current slide that's being shown on a display is has been replaced by another slide.
If there's an exit transition, this method is called AFTER the transition starts. In other words, it means the animation will be NOT playing as the slide transition is happening.
You might wonder what this is for, since what's the point of an animation if the slide is not showing? This is useful if you want to pause or reset an animation when the slide is not active. Then you can resume or restart the animation with the "pre_show_slide" or "show_slide" event when the slide is shown again.
This event is triggered when the slide this widget is part of is played
as part of a
slide_player: "play" command, either via a standalone
slide player config or as a show step).
Other slide-related MPF events
In addition to the seven special-purpose animation trigger events listed above, there are three standard MPF events which are posted when slides are created, when they become active, and when they're removed. See the events reference for details on when these three events are posted.
4. Animating multiple properties at once
The example animation above includes two steps (one to set the opacity
to 1 and the next to set it to 0). By default steps are sequential,
meaning that one step completes before the next one starts. However you
can add a
timing: with_previous to an animation step which will make
it so that step runs at the same time as the step before it. This means
you can animate multiple properties at once.
For example, to make the text grow and shrink while also fading on and off:
slides: slide_1: widgets: - type: text text: MY TEXT color: red font_size: 50 animations: show_slide: - property: opacity value: 1 duration: .5s - property: font_size value: 100 timing: with_previous # makes this step run at the same time as the previous one duration: .5s # specify a duration for each step, even when with_previous - property: opacity value: 0 duration: .5s repeat: true - property: font_size value: 50 duration: .5s
Notice that the animation in the example above has 4 steps, but steps #2
and #4 have the setting
timing: with_previous. You can chain together
with_previous steps as you want. (The default setting for one
step to run after the previous one is
timing: after_previous, but
since that's the default you don't need to explicitly add it.
Also note that all 4 steps above specify
duration: .5s. However you
can make each step a different amount of time. In fact you can even make
with_previous steps different durations (though the animation
won't move on to the next
after_previous step until all the
simultaneous steps are complete).
By the way, the example above is a widget that's part of a slide, but
remember you can add animations to widgets anywhere a widget is defined
(in the slide properties, in a show step, as part of a
named widget, as part
widget_settings: override section in the
It is also possible to animate multiple properties in a single animation
step by using a list in both the
(there must be the same number of items in both lists). The following
example moves a widget diagonally to the coordinate (10, 20) while
rotating it 180 degrees over 5 seconds:
- property: x, y, rotation value: 10, 20, 180 duration: 5s
5. Multi-step animations with different trigger events
So far all of the animation examples have been triggered on the
show_slide event (which means they start animating as soon as the
slide is shown).
You can create multiple event entries in the animation that cause different animations to take place when different events occur. You can mix and match these as much as you want, including mixing the "special" animation trigger events with regular MPF events.
slides: slide1: widgets: - type: text text: I'M GOING TO MOVE x: 50 y: 50 animations: move_up: property: y # if there's just one animation step, we don't need the hyphen value: 100 move_down: property: y value: 0 move_right: property: x value: 100 move_left: property: x value: 0 move_home: - property: x value: 50 - property: y value: 50 timing: with_previous
In the above example, we have five different animation events
configured. These are just regular MPF events which you can use from
logic blocks, shots, switch events, etc. When the event
posted, this widget will move to the top of the display (
x: 100), when
move_left event is posted, it will move to the left of the screen,
move_home is posted, there are two steps in the animation which
both run together to move the widget back to its initial position.
Again, you can use any combination of properties and any number of steps for each event.
You can also use a property from your event. For instance, you can move a widget based on a player variable:
slides: slide1: widgets: - type: text text: I'M GOING TO MOVE x: 50 y: 50 animations: player_loops: # this is posted when the player variable "loops" changes property: y value: (value) # this is the value of loops
6. Looping and repeating animations
So far, every animation sequence we've looked at will just run through
once and then stop. However, you can add
repeat: true (or
repeat: yes) to the last step of an animation, and that will cause
that animation to loop back to the beginning and keep repeating.
Of course you can mix-and-match repeating animations with one time animations. For example:
slides: slide1: widgets: - type: text text: BOO! y: -50 font_size: 90 animations: show_slide: property: y value: 50 duration: 500ms pulse_boo: - property: font_size value: 100 duration: 250ms - property: font_size value: 90 duration: 250ms repeat: true bye_boo: - property: y value: 100 - property: x value: 150 timing: with_previous
In the example above, when the slide is shown (or when the widget is
added if this config was in your
widgets: section and you added it via
widget_player: entry), the widget will fly into the slide from the
bottom (since the initial y value is -50, it will start off the screen).
Then when the
pulse_boo event is posted, the two-step animation which
makes the font size bigger and smaller will starting playing and repeat
forever. Finally when
bye_boo is posted, the widget will fly off the
screen to the upper right.
There is something special to consider when working with 360 degree rotations. Setting the property rotation to 360 results in a complete turn of a widget. However, attempting to repeat the 360 degree rotation appears to do nothing. This is because of how Kivi handles 0 and 360 degrees. When the rotation completes, the widget is at rotation 360 degrees. Repeating the step fails to cause the widget to move because it is already at destination. To create continuously rotating widgets, a two step process is required:
slides: slide1: widgets: - type: text text: I'M GOING TO ROTATE x: 50 y: 50 animations: show_slide: - property: rotation value: 0 relative: false duration: 0 - property: rotation value: 360 relative: false duration: 2s repeat: true #! slide_player: #! show_slide: slide1 ##! test #! post show_slide
The first step creates a reset point setting the widget at 0 degrees. This assures that there is always a destination to rotate to which is provided by the following step.
7. Inserting a "pause"
Sometimes you might want to add a timed "pause" to an animation, where one step animates, then it pauses, then another step animates.
The easiest way to do that is just to add a step where the property value in the step is the same as whatever value that property is currently at. This is easy to do using a relative property value of 0 as shown in the following example. So you still have the step in the animation, it just isn't doing anything since the widget's property is already at the desired target value. For example:
slides: slide1: widgets: - type: image image: flying_toaster y: -50 animations: show_slide: - property: y value: 50 duration: 1s - property: y value: 0 relative: true duration: 2s - property: y value: 200
The the example above, the
flying_toaster image will move in from the
bottom of the screen (to
y:50) in 1 second, then pause for 2 seconds
y: 50 again), then move out of the top of the screen in 1
You can also set "easing" values for each animation step which
controls the formula that's used to interpolate the current value to
the target value over time. The default is
linear which just does a
constant motion (no acceleration/deceleration) over time. Refer to the
Easing Instructions for details on
how this works and descriptions of all the options.
9. Creating reusable "named" animations
Much like named widgets, you can also create pre-defined animations that you can
easily apply to any widget. You do this by adding those animations to
animations: section of your config, like this:
animations: fade_in: property: opacity value: 1 duration: 1s fade_out: property: opacity value: 0 duration: 1s
Now you can use these animations, by name, in any widget or widget_player config where you would ordinarily define your own animations.
For example, to configure a widget to fade in (assuming the widget was
initially created with
widgets: hello_widget: - type: text text: HELLO animations: show_slide: fade_in
Again remember this can be done anywhere you configure an animation. So if you later wanted to fade that text out when the event "timer_hurry_up_complete" is posted, you can do it like this:
widgets: hello_widget: - type: text text: HELLO animations: show_slide: fade_in timer_hurry_up_complete: fade_out
10. Chaining multiple named animations together
When working with named animations, you can chain together multiple named animations for a single event by specifying them as a list, like this:
widgets: hello_widget: - type: text text: HELLO animations: some_event: fade_in, fade_out, pulse
Any animation with
timing: with_previous in the first step will run
with the previous one, meaning you can create lots of little effects and
sub-animations and then combine them in reusable ways throughout your
You can even use the same animation over and over in a sequence to repeat something a certain number of times. For example:
animations: pulse: - property: opacity value: 0 duration: 100ms - property: opacity value: 1 duration: 100ms timing: after_previous widgets: widget1: - type: text text: HELLO animations: flash_3x: pulse, pulse, pulse
In the example above, when the MPF event "flash_3x" is posted, it will cause widget1 to pulse three times.
11. Animating a progress bar
MPF can also animate progress bars or similar things. In this example,
we animate a progress bar based on the player variable
hooking the width of the bar to the event
player_progress which is
posted when the value changes:
slides: green_slide: widgets: - type: rectangle y: 50 z: 20 anchor_x: left anchor_y: center width: 0 height: 16 corner_radius: 3 corner_segments: 3 color: 00FF00 animations: player_progress: - property: width value: (value)
Something missing or wrong? You can fix it!
This website is edited by people like you! Is something wrong or missing? Is something out of date, or can you explain it better?
Please help us! You can fix it yourself and be an official "open source" contributor!
It's easy! See our Beginner's guide to editing the docs.
Page navigation via the keyboard: < >
You can navigate this site via the keyboard. There are two modes:
General navigation, when search is not focused:
- F , S , / : open search dialog
- P , , : go to previous page
- N , . : go to next page
While using the search function:
- Down , Up : select next / previous result
- Esc , Tab : close search
- Enter : go to highlighted page in the results