Anima is an attempt at an animation framework entirely in Elm and intended to be used with the Elm architecture. This library is the product of an experiment to arrive at an architecture using types and the algebra around them as functional thinking tools. Anima encompasses normal "fixed" animations, retargetable/additive animations as well as physics-based animations. The core library is about 500 lines of Elm code and includes an opinionated notion of application architecture that's an elaboration of the Elm architecture. The (current) purpose of the library is not so much as a production grade animation library, but as an exploration of concepts.
Some notes on the approach and motivation are also available in this blog post.
Status: Work in progress. Unoptimized. Use at your own risk.
- Red box (.elm)
- Photo viewer (.elm)
- Red box, but using physics (.elm)
- The famous Example 8 (.elm) from The Elm Architecture tutorial.
A more sophisticated series of examples demonstrating progressive addition of animation and behaviour to an application while keeping the core application model unmodified --
veggies1 (.elm) Presents the core interface which has two lists of labels - "fruits" and "veggies". Click on a label to move it to the other list. The idea is to sort the two lists to have the fruits correctly under the fruits list and the veggies under the veggies list. The program doesn't verify that you've actually done so, but just lets you play around.
veggies3 (.elm) Functionally the same as the previous - i.e. you move labels from one list to the other by clicking on them. However, the movement is now smoothly animated. You can click as fast as you want and even on elements in flight, if you're that fast. In other words, these are retargetable animations.
veggies4 (.elm) A bit richer functionality. You can reorder within a list or across lists by dragging and dropping labels. Once dropped, the items all animate into their new positions smoothly. Also, you get indicators showing what's been picked up and where it will be dropped if you let go of the mouse picker. This is a precursor to full multi-touch interactions.
The framework treats an "app" as a wiring up of four processes - a "modeller", "director", "animator" and "viewer".
The Modeller is a process that maintains a "model" in response to user input. The data in this model is typically what the user cares about. It does not include any presentation related information.
modeller : Automaton input model
The Director is a process that provides an indication of the stable state of the view in response to input and the updated model. The director does not worry about the details of how this stable state of the UI is going to be achieved.
director : Automaton (input, model) direction
The Animator is a process that takes the indications of the director and worries about how to reach the indicated stable state. Its output is detailed instructions for "what should be shown right now". While it may look like the distinction between the director and the animator is merely that of role, the runtime treats them on different schedules. The director gets to process all input events, including frame step clock ticks. So it may execute an arbitrary number of times every second. However, the animator runs at 60fps
- i.e. only once on every frame step.
animator : Animation direction viewstate
The Viewer takes the instantaneous instructions of the animator and shows the UI. This can usually be a pure function that computes a
viewer : Automaton (model, viewstate) output
An animation is thought of as a process that takes an input value valid for a small time interval and produces a corresponding output value for the same time interval.
type alias Animation input output = Automaton (TimeStep, input) (TimeStep, output)
Physics based animations are built around the notion of a "particle", which is seen
as a process that responds to a number of forces by updating its position in a
"phase space" consisting of the pair
type alias Particle space = Animation (List (Force space)) (PhasePos space) type alias PhasePos space = (space, space) type Force space = Drag space -- Drag positionVectorChange | SomeForce space -- SomeForce forceVector | Kick space -- Kick impulseVector | Wall space Float space -- Wall normalVector coeffOfRestitution pointOnWall | Buff space space -- a wall where coeffOfRestitution is zero | Friction Float Float -- Friction restSpeed fricCoeff | Spring space Float Float -- Spring anchorPoint hookeConstant dampingFactor | Gravity space -- Gravity gvector
picker is an automaton that tracks drag and drop operations. One item
at a time can be in the "being picked and dragged" state and the picker
continuously outputs information about what is currently picked, if any,
and if so how far has it been moved.
type PickerAction = PickupItem String | MoveItem | DropItem type alias Picker space = Automaton (space, Maybe PickerAction) (Maybe (String, space, Bool)) picker : Picker Point2D
The input is the pair
(mousePos, action) and the output is
an optional triple of
(key, moveExtent, active). The
will turn false the moment the picker is dropped and will be true as long
as the item remains "picked".
PPS: Drag-n-Drop using these concepts does not use the HTML drag events. Instead, it works directly with the mouse events. For the moment, I think this is better than the HTML drag events since we're not limited to dragging only one thing at a time ... though I'm yet to build a picker that supports multitouch.
PS: The "particle collection" concept uses a
Dict, which I think is not
the most efficiently implemented, but will probably suffice for these experiments.
Animations apply to continuous values, although there are generalizations to discrete values too. We need to be able to do some common calculations on these values such as linear interpolation, find distances and apply dynamics. The values can be one, two or three dimensional, or can be colour. The mathematical notion of a vector space is useful in this regard.
To capture this variety of what can be animated and the nature of the
calculations that animations entail, the module
Space introduces the abstract
notion of a continuous space of points that we move smoothly though. The other
animation concepts rely on this abstraction.