Forces
Like Middleware, Forces allow us to define custom functions that apply to a tween.
The previous lesson stated that Middleware is applied at the Timeline level, and converts non-tweenable values (stings etc.) to tweenable values (numbers).
Forces differ in that they are applied at the Shape level. Their role is to adjust the value of a Shape's points and/or attributes post-tween.
Why might we want to adjust a Shape's values?
The main use case for a Force is to apply an "external force" to a Shape. An external force could be a motion path, rotation or physics.
Let's take the case of a motion path. Normally, when we animate a Shape from position A to position B, the movement between the two positions will occur in a straight line. To move the Shape along a motion path instead, we can use a Force. This Force would offset the Shape depending on its position in the tween.
Create a force
A Force is just a function that is called on each tick of a tween. The function receives two arguments.
- A Frame Shape – the data required to render a Shape. A Frame Shape is calculated by tweening between two Keyframes. It takes the form of an object that can contain three properties.
points– shape data as defined by Points.attributes– an object of additional Shape attributes (eg.fill).childFrameShape– an array of child Frame Shapes.
- A Frame Position – a number (between 0 and 1 inc.) representing the Shape's current progress between two Keyframes.
A Force must return a Frame Shape.
As an example, let's create a Force called shakeForce, that randomly adjusts the x and y value of each point in a Shape.
import { shape, timeline, render, play } from 'wilderness'
const randomlyAdjustPoint = point => ({
...point,
x: point.x + Math.floor(Math.random() * 10) - 5,
y: point.y + Math.floor(Math.random() * 10) - 5
})
const shakeForce = ({ points, attributes, childFrameShapes }, framePosition) => ({
points: points.map(randomlyAdjustPoint),
attributes,
childFrameShapes
})
const plainShapeObject = {
type: 'rect',
x: 5,
y: 5,
width: 10,
height: 10,
fill: '#1F9FFD'
}
const keyframe1 = plainShapeObject
const keyframe2 = {
...plainShapeObject,
transforms: [[ 'offset', 80 ]],
forces: [ shakeForce ]
}
const shakyRect = shape(keyframe1, keyframe2)
const animation = timeline(shakyRect, {
duration: 2000,
iterations: Infinity,
alternate: true
})
render(document.querySelector('svg'), animation)
play(animation)
Wowzers. What a shaky little guy.
Notice in the example above that we add Forces to a Shape by defining a forces array on the Keyframe we are tweening to.
Force helpers
Wilderness has a couple of Force function helpers built in.
- ⚡️ Motion path.
- 🎡 Rotate (coming soon).