-
Notifications
You must be signed in to change notification settings - Fork 477
New Animation API
We are about to release a new animation api.
https://github.com/koenbok/Framer/pull/420 https://github.com/koenbok/Framer/issues/378
- General documentation
- API documentation
- Change (old/new) examples
===
This is a current relevant overview of how the API will be from now on. For more information about what changed, see below.
- properties or state: either an object with property values you want to animate to, or an existing state name.
-
options: options for the animation (see
animationOptions
).
This creates a new animation for a layer based on a state or new property values and starts it immediately. If the properties have an options
key, it is used as options for the animation.
If there is currently an animation on the layer, it gets stopped if it is animating the same properties as the new animation. Simply put, no two animations can be ran on width at the same time, but you can have two separate animations, one running on width and the other on height.
Simple examples for animating of properties.
# Properties animation
layer.animate
x: 200
# Properties spring animation
layer.animate
x: 200
options:
curve: "spring"
Simple examples for animating with states.
layer.states.test =
x: 200
# State animation
layer.animate "test"
# State animation with options
layer.animate "test",
curve: "spring"
Stop all animations for this layer.
Animation objects manage animations that target a layer and properties. An animation will tween between a start and end value, with a curve. The start value is determined when the animation starts, and the end value is defined by properties. If the start values equal to end values, they won't animate.
-
curve
— A string, set to ease by default. (Optional) -
curveOptions
— An object with the options of the set curve. (Optional) -
time
— A number, the duration in seconds. (Optional) -
delay
— A number, the delay of the animation. (Optional) -
repeat
— A number, the amount of times it repeats. (Optional) -
colorModel
— A string, the model to animate colors in. (Optional) -
instant
— A boolean, don't animate. (Optional)
The states object on a layer holds all the different states for a layer. They can be used to animate to, switch to, or cycled through. States are a great way to organize different visual properties of layers.
The properties of a state can be anything on a layer like x
, y
, etc. Properties that cannot be animated like html
or visible
can still be used but will not be animated. They will get set at the end of a transition.
States can have a special options
key to hold animation options like curve
. They will be used whenever layers get animated to that state.
There are two special states: current
and previous
. They refer to the current and last state that a layer is in. Additionally, the have a name
key so you can check the current state name with layer.states.current.name
.
# Set a single state
layer.states.stateA =
x: 100
# Animate to the state
layer.animate("stateA")
# Go to the state without animation
layer.stateSwitch("stateA")
Here is a common example where we set multiple states and cycle through them.
# Set multiple states at once
layer.states =
stateA:
x: 100
stateB:
x: 200
# On a click, go back and forth between states.
layer.onTap -> layer.stateCycle(["stateA", "stateB"])
[todo]
The animation class explicitly creates an animation for a layer. This is the exact same as using layer.animate()
, but it does not start the animation immediately, you explicitly have to call start.
layerA = new Layer()
animation = new Animation(layer, {x: 500}, {curve: "spring"})
animation.start()
Change a property.
layer = new Layer
## After
layer.animate
x: 100
## Before
layer.animate
properties:
x: 100
Change a property with options.
## Before
layer.animate
properties:
x: 100
time: 0.5
## After
layer.animate
x: 100
options:
time: 0.5
Change the animation curve.
## Before
layer.animate
properties:
x: 100
curve: "spring(250, 50, 0)"
## After
layer.animate
x: 100
options:
curve: "spring(250, 50, 0)"
Add a single state.
## Before
layer.states.add
stateA:
x: 100
## After
layer.states.stateA =
x: 100
Define multiple states at once.
## Before
layer.states.add
stateB:
x: 200
stateC:
x: 400
## After
layer.states =
stateB:
x: 200
stateC:
x: 400
Notice the subtle difference between calling a function and setting a property. This means that where previously it was possible to add multiple states multiple times, in the new API we will override the existing states when calling layer.states = ...
again. However, this is really unlikely and one could still achieve this by doing:
layer.states =
stateA:
x: 100
stateB:
x: 200
layer.states = _.extend layer.states,
stateC:
x: 300
stateD:
x: 400
Animate to state.
## Before
layer.states.switch "stateA"
## After
layer.animate "stateA"
Animate to state with options.
## Before
layer.states.add
stateE:
x: 200
layer.states.switch "stateE",
curve: "ease-in"
## After
layer.states.stateE =
x: 200
layer.animate "stateE",
curve: "ease-in"
States can now also include optionsin the state itself. This is handy for declaring how to animate to a state as well. This has the exact same result as above.
layer.states.stateE =
x: 200
options:
curve: "ease-in"
layer.animate "stateE"
Switching instantly will become an option of the animation
## Before
layer.states.switchInstant "stateB"
## After
layer.stateSwitch "stateB"
## Which will be a shorthand for:
layer.animate "stateB",
instant: true
This means it can also be defined directly in a state itself:
layer.states =
stateA:
x: 100
options:
instant: true
Next has been renamed to cycling, because next was pretty ambigious depending on the context.
## Before
layer.states.next()
layer.states.next("stateB","stateC")
## After
layer.stateCycle()
layer.stateCycle(["stateB", "stateC"]) # Preferred
layer.stateCycle("stateB", "stateC") # Also valid
# With options
layer.stateCycle ["stateB", "stateC"],
time: 0.5
Notice how we use an array of states names here, to support animation options as second argument
There are three special states that will be set automatically and can't be overridden:
-
layer.states.default
- The state the layer had upon creation. -
layer.states.previous
- The previous state the layer was in -
layer.states.current
- The current state the layer is in
These states contain the actual values and not (as is the case with layer.states.current
now) the state string. The name of the previous an current states will still be available through layer.states.previous.name
and layer.states.current.name
. The name
key only gets added in the context of these special states, so the special state objects are not the same as the actual state object. They are completely equal, with the name key added.
Notice the absence of layer.states.next
, this functionality will be provided by layer.stateCycle()
as described above.
We will add a new property layer.stateNames
that lists all the names of states currently defined on a layer. This list will contain layer.states.initial
, but won't contain default
and current
.
layer.states =
left:
x: Align.left
right:
x: Align.right
layer.animateToNextState()
print layer.states.current.name # "left"
print layer.stateNames # ["default", "left", "right"]
The states on a layer is pretty much a normal object. So you can use different ways to loop through the states.
layer.states =
left:
x: Align.left
right:
x: Align.right
# All thsese print ["default", "left", "right"]
print layer.stateNames # ["default", "left", "right"]
print Object.keys(layer.states)
print _.keys(layer.states)
print (k for k of layer.states)
# But beware of this one, it is empty: []
print (k for k in layer.states)
===
Some refactoring for the new animation api
- Move state switching responsibility back to the
LayerStateMachine
. - Clean up animation events;
AnimationStart
,AnimationStop
andAnimationEnd
. - Deprecate
StateWillSwitch
andStateDidSwitch
in favor ofStateSwitchStart
,StateSwitchStop
andStateSwitchEnd
. - Refactored
LayerStates
to use prototype properties, so we don't make them in constructor.
- You cannot override the deprecated names anymore (like
.add
) to use as a state name, because they would not show up in state names. - Remove the
initial
state and keepdefault
as is.
- Ideally
Object.keys(layer.states)
should be the same asfor k in layer.states
but that does not seem to be possible. - Maybe we need to change
layer.states.current
back from the name to the actual properties and add a name if you return them so you can dolayer.states.current.name
. This would lose comparison by reference, though. - There is a (double) circular dependency between
Layer
→LayerStates
→LayerStateMachine
and back. Not the end of the world, but I'm not sure if the GC is happy about this.
See: https://github.com/motif/company/issues/2633, https://github.com/motif/company/issues/2635