Documentation
¶
Overview ¶
Package anim is the arithmetic behind things that move.
It is pure functions of a tick and an eased value that advances one step at a time. Nothing here holds a clock, opens anything, or knows what a cell is: the loop decides when time passes, and what these numbers are then used for — brightening a colour, growing a pane — is the caller's.
Why ticks rather than durations ¶
A transition measured in wall-clock time has to ask what time it is, and a widget that asks what time it is cannot be stepped by a test or paused by a loop that parked because nothing was happening. Everything here counts ticks instead, and a tick is whatever the caller decided one is when they started a clock with Loop.Every. That makes an animation exactly as deterministic as the thing driving it, which is the property the rest of this library is built on.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EaseOutCubic ¶
EaseOutCubic maps a linear position in [0,1] to an eased one.
Out-cubic rather than anything else because it is the curve that reads as "arrived": most of the distance is covered early and the last part settles, which is what makes a pane that grows look like it stopped rather than like it was cut off.
func Shimmer ¶
Shimmer is the brightness at one column of a row being swept by a moving highlight — the "this is still arriving" effect on streamed text.
pos is the column, width the row's width, and the result is in [0.35, 1]. It is meant for [grid.View.Fade]: fade the column by one minus this, and the sweep reads as light rather than as characters changing. A fade rather than a colour, because what the text dissolves into is whatever that column is drawn on.
Types ¶
type Keyframe ¶ added in v0.0.2
type Keyframe struct {
// At is how many ticks into the timeline this value is reached.
At uint64
// Value is what it is then.
Value float64
}
Keyframe is a value a Timeline passes through, and when.
type Spring ¶ added in v0.0.2
type Spring struct {
// Frequency is how fast it moves, in radians per tick: higher is stiffer. Zero
// uses the default.
Frequency float64
// Damping is how much it overshoots. One arrives and stops; below one it goes
// past and comes back, and the further below, the more it bounces; above one it
// creeps in slowly. Zero uses the default, which is one.
Damping float64
// contains filtered or unexported fields
}
Spring moves a value toward a target the way a weight on a spring does: it gathers speed, arrives, and — if it is allowed to — goes a little past and comes back.
It is the other kind of movement from Transition, and the difference is what happens when the target changes half way. A transition restarts, easing from wherever it was, and the change reads as a stop and a fresh start. A spring keeps the speed it already had, so a target that moves twice reads as one continuous movement — which is what makes it the right thing for anything the user is dragging, resizing or scrolling.
Like everything here it is stepped in ticks rather than in seconds, so a test steps it and gets the same numbers every time.
The zero Spring is settled at zero and moves nothing. Given a target it uses the defaults above, which arrive without overshooting.
func (*Spring) Done ¶ added in v0.0.2
Done reports whether the spring has arrived: near enough its target and slow enough that nothing further would be visible. It is what tells a loop it can stop the clock driving this, and it is the same question Transition.Done answers.
func (*Spring) Set ¶ added in v0.0.2
Set puts the value where it is asked and stops it there, which is how to place a spring without animating it — the first frame, or a resize nobody should watch.
func (*Spring) Tick ¶ added in v0.0.2
func (s *Spring) Tick()
Tick advances the spring by one step.
The step is the exact solution of the equation rather than a small step of it, which matters at a terminal's frame rate: stepping a stiff spring approximately, sixty times a second, is how a spring turns into an oscillation that grows instead of one that dies away. There are three solutions and which one applies is decided by the damping — under one it rings, at one it just arrives, above one it creeps — so all three are here rather than the one that is usually enough.
func (*Spring) To ¶ added in v0.0.2
To sets what the spring is moving toward, keeping the speed it already has.
type Timeline ¶ added in v0.0.2
type Timeline struct {
// Frames are the values it passes through, in the order their ticks say. They are
// read as given: frames out of order are the caller's mistake and are left as
// such, because sorting them would hide it.
Frames []Keyframe
// Ease shapes the movement between two frames. Nil is linear, which is what a
// sequence of steps usually wants — each step already says how long it takes, and
// easing every one of them separately reads as a stutter.
Ease func(t float64) float64
// Loop starts again from the beginning once the last frame is reached, which is
// what anything idling — a pulse, a sweep — is made of.
Loop bool
// contains filtered or unexported fields
}
Timeline is a value that follows a sequence of keyframes: it eases from each to the next and holds the last one.
It is the third kind of movement here, and the one the other two cannot express. A Transition goes from one value to another and a Spring chases a target; a sequence — appear, hold, fade — is neither, and writing one out of transitions means keeping a step number and a counter beside them and remembering to advance both.
The zero Timeline holds zero for ever, which is what a caller who never set any frames should get.
func (*Timeline) Done ¶ added in v0.0.2
Done reports whether the last frame has been reached. A looping timeline is never done, which is the honest answer: nothing is waiting for it.
func (*Timeline) Reset ¶ added in v0.0.2
func (t *Timeline) Reset()
Reset takes the timeline back to its beginning.
func (*Timeline) Span ¶ added in v0.0.2
Span is how long the whole timeline is, which is when its last frame is reached.
func (*Timeline) Tick ¶ added in v0.0.2
func (t *Timeline) Tick()
Tick advances the timeline by one step. It is safe to keep calling after the end, which is what lets a caller drive several from one clock without checking each.
func (*Timeline) Value ¶ added in v0.0.2
Value is where the timeline is now.
Before the first frame it is the first frame's value, and after the last it is the last one's — a timeline holds its ends rather than running off them, so a caller that draws one tick late draws the end state instead of nothing.
type Transition ¶
type Transition struct {
// contains filtered or unexported fields
}
Transition moves a value toward a target over a number of ticks, eased.
The zero Transition is settled at zero and animates nothing, so a caller who never starts one pays for nothing. Transition.Tick is what advances it, and a caller with several can drive them all from the one clock they already have.
func (*Transition) Done ¶
func (t *Transition) Done() bool
Done reports whether the transition has arrived, which is what tells a loop it can stop the clock driving it.
func (*Transition) Tick ¶
func (t *Transition) Tick()
Tick advances the transition by one step. It is safe to keep calling after it has arrived, so a caller does not have to check before stepping.
func (*Transition) To ¶
func (t *Transition) To(target float64, span uint64)
To retargets the transition, starting from wherever it is now. A span of zero arrives immediately, which is how to set a value without animating it.
func (*Transition) Value ¶
func (t *Transition) Value() float64
Value is where the transition is now.