Action
Action is a simplified Rx-inspired reactive stream focused on animation.
Every Popmotion animation and input is an action.
When an action is started, it returns a simple interface that includes at least a stop
method.
Import
import { action } from 'popmotion';
Usage
Definition
The action
factory takes one argument, an init
function.
This is a function that is provided an object of update
, complete
, and error
functions.
Usage of these functions is optional. Your action may call all or just some of them:
action(({ update, complete, error }) => {
update(1);
});
Initialisation
action
returns a start
method. This also accepts an object of update
, complete
, and error
functions.
When called, the init
function is provided these functions, and a new instance of the action is created.
Calling start
multiple times will create multiple, separate instances of the action.
For example:
const foo = action(({ update }) => {
let i = 0;
setInterval(() => update(i++), 50);
});
foo.start({
update: (v) => console.log(v)
}); // 0, 1, 2...
If start
is passed only a function, that is assigned to the update
function:
foo.start((v) => console.log(v)); // 0, 1, 2...
We can also listen for the complete
event like this:
const foo = action(({ update, complete }) => {
let i = 0;
setInterval(() => {
update(i++);
if (i === 10) complete();
}, 50);
});
foo.start({
update: (v) => console.log(v), // ...8, 9, 10
complete: () => console.log('complete!')
});
Interface
The init
function can optionally return an API.
For instance, we might use this to stop a timer:
const foo = action(({ update }) => {
const interval = setInterval(() => update('ping!'), 100);
return {
stop: () => clearInterval(interval)
};
});
const bar = foo.start(console.log);
setTimeout(() => bar.stop(), 1000);
Any method returned by the action init
function will be exposed when an action instance is created.
Modification
action
is chainable, which means it offers methods that can alter the behaviour of the base action. Currently, these are while
and pipe
(see Methods).
When an action is chained, a new action is returned. For instance:
const foo = action(({ update }) => {
let i = 0;
setInterval(() => update(i++), 50);
});
const lessThanTen = (v) => v < 10;
const log = (v) => console.log(v);
foo.start(log); // ...8, 9, 10, 11...
foo.while(lessThanTen).start(log); // ...8, 9
Methods
pipe
pipe(...funcs: (v: any) => any)
Returns a new action that passes the output of the original action’s update
through the provided functions, from left to right.
Example
const init = ({ update }) => update(10);
const double = (v) => v * 2;
const px = (v) => v + 'px';
action(init)
.pipe(double, px)
.start((v) => console.log(v)); // '20px'
start
start(update: (v: any) => void)
start({
complete? () => void,
error?: (err: any) => void,
update?: (v: any) => void
})
start(reaction)
Starts the action by running its initiation function, and returning its API.
Every interface returned by a start
call, regardless of the API returned from the observable, will return at least a stop
function.
It can be provided either an update
function, or an object with update
, complete
and error
functions.
Example
// Doesn't return an API
const foo = action(({ update }) => update(1)).start();
foo.stop();
// Returns a custom API
const bar = action(({ update }) => {
let i = 0;
setInterval(() => update(i), 100);
return {
setOutput: (v) => i = v
};
}).start();
bar.setOutput(2);
setTimeout(() => bar.stop(), 1000);
while
while(predicate: (v: any) => boolean)
Returns a new action, that will continue to run while the updated values match the provided predicate.
When the predicate function returns false
, the action will complete
.
Example
let latest = 0;
const init = ({ update }) => {
let i = latest;
setInterval(() => update(i++), 50);
};
action(init)
.while((v) => v < 10)
.start({
update: (v) => latest = v,
complete: () => console.log(v) // 9
});