Proposal: New Animation & States API
See original GitHub issueWe’re changing the way animations are defined and how they interact with states. There are a couple of reasons for this:
- There are two ways to animate a layer now: With
layer.states.switch
andlayer.animate
, we would like to combine them - The current way of animating a layer directly needs the
properties:
keyword, which is confusing for beginners - It’s not obvious how to specify animation options for state switches
This proposal tries to provide a solution for the above problems. please provide us with your feedback in the comments!
Basic animation of a layer
Just change a property
layer = new Layer
## Before
layer.animate
properties:
x: 100
## After
layer.animate
x: 100
Change the animation timing
## 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)"
States
Add a single state
## Before
layer.states.add
stateA:
x: 100
## After
layer.states.stateA =
x: 100
Define multiple states
## 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"
Add animation options to a state change
The options for an animation can be provided in a state as well
## Before
layer.states.add
stateE:
x: 200
layer.states.switch "stateE",
curve: "ease-in"
## After
layer.states.stateE =
x: 200
options:
curve: "ease-in"
layer.animate "stateE"
Instantly switch to a state
Switching instantly will become an option of the animation
## Before
layer.states.switchInstant "stateB"
## After
layer.switchInstant "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
Move to the next state
## Before
layer.states.next()
layer.states.next("stateB","stateC")
## After
layer.animateToNextState()
layer.animateToNextState ["stateB","stateC"] # Preferred
layer.animateToNextState "stateB","stateC" # Also valid
layer.animateToNextState ["stateB","stateC"],
time: 0.5
Notice how we use an array of states names here, to support animation options as second argument
Special states
There are three special states that will be set automatically and can’t be overridden:
layer.states.initial
- The state the layer had upon creation. This will contain the properties that are provided to the constructor, not ones that were set in a layer stage (i.e. withlayer.height = 100
)layer.states.previous
- The previous state the layer was inlayer.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.previousName
and layer.states.currentName
.
Notice the absence of layer.states.next
, this functionality will be provided by layer.animateToNextState()
as described above.
Listing all the states
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 previous
and current
.
layer.states =
left:
x: Align.left
right:
x: Align.right
layer.animateToNextState()
print layer.states.currentName # "left"
print layer.stateNames # ["initial", "left", "right"]
Determinism
Out of this proposal, moved to #384
Is the previous state API the order state changes occurred influenced the resulting position of the layer. Consider the following example:
background = new BackgroundLayer
layer = new Layer
layer.states.add
right:
x: Align.right
bottom:
y: Align.bottom
left:
x: Align.left
top:
y: Align.top
background.onClick ->
layer.states.next()
(Also available here: http://share.framerjs.com/ff6vwjpn0wet/)
Because not all states define all properties, you can end up with some in-between state (try clicking quickly in the example). Therefor we would like to make states deterministic. That is: every state defines every property. If you omit a property during the definition of the state, we use the initial state as the default value for that property.
Examples of this can be found here: http://share.framerjs.com/kthdx2kmp00m/
The red layer shows deterministic states, but all derived from the initial states, this layer will always end up in one of the corners, but never reach the bottom right corner
The blue layer has deterministic states, and defines the x
and y
for every state, resulting in the intended behaviour.
Details
The formal API of layer.animate
will be:
layer.animate(properties, options)
However, by adding the options key to the properties object, you can take a shortcut that looks nicer in coffeescript:
layer.animate
x: 100
options:
curve: "spring"
delay: 1
The same will work for states, so animation options can be specified directly in the state:
layer.states.stateD =
y: 100
options:
delay: 1
time: 0.25
layer.animate "stateD"
## But could also be used like this:
layer.animate layer.states.stateD
## When options are provided, they override the options of the state
layer.animate "stateD",
delay: 0 # Delay will be 0 and not 1
It used to be possible to set layer.states.animationOptions
to change the animation option of every state change. This will be broadened to set the animationOptions for all animations on a layer:
## Before
layer.states.animationOptions =
curve: "spring"
## After
layer.animationOptions =
curve: "spring"
Pitfalls
Common pitfalls we expect with the new approach, so we should have excellent documentation and (if possible) error messages for them:
- Setting one of the preserved state names:
initial
,previous
, orcurrent
- Checking
layer.states.current
expecting a string
Issue Analytics
- State:
- Created 7 years ago
- Reactions:19
- Comments:33 (8 by maintainers)
Top GitHub Comments
This has just been merged into master. The changes are fully backwards compatible, so every project should keep working if you choose ‘Update Framer Library’
Yes we do. Not only can the options potentially clash with properties on the layer, we also need a hook to support Auto-Code for animation options in the future