Popmotion是一款轻量级动画库,它在2017年火得一塌糊涂,so,开始我们的popmotion之旅吧,具体动画体验可以去官网demo,这里主要讲述popmotion方法和原理
新手教程里面说到的tween
方法,是作为一个action返回的
Popmotion是一个响应式的组件库,并且action是一个创建流的函数,这些action输出到一个reactions
在Popmotion中每个动画和输入都是一个action,在此博客中,我们将用实例讲解什么是action和reactions
import { action } from ‘popmotion’;
action函数接受一个参数。它是一个初始化函数,每次通过它的start方法启动返回的操作时都会执行它。
这意味着我们可以定义一个操作,并多次启动它,从而导致该操作的多个实例。
初始化函数是由三个函数组成的对象:update,complete,error
action(({ update, complete, error }) => {})
让我们来定义一个叫做just
的函数。它将返回一个操作,在启动时,该操作将用提供的值触发更新,然后完成:
const just = (v) => action(({ update, complete }) => {
update(v);
complete();
});
现在,当刚开始返回的操作开始时,它将发出提供的值
just(1).start(console.log);
console.log
方法被当做了reactions
,每当新操作实例用新值调用UPDATE时,它都会触发。
我们还定义了只在完成后才能启动。我们可以提供一个更新和完成函数的对象,而不是一个函数,作为我们的反应reactions
just(1).start({
update: console.log,
complete: () => console.log('complete!')
});
开始运行时,重新运行初始化函数,并返回活动的action
新实例
const justOne = just(1);
justOne.start(console.log); // 1
justOne.start(console.log); // 1
由于所有PopMotion动画都是action
,我们可以定义动画一次并多次使用它
const mySpring = spring({ to: 500 });
mySpring.start({
update: console.log,
complete: () => console.log('complete!')
});
mySpring.start({
update: (v) => console.log('second spring: ' + v),
complete: () => console.log('second spring also complete!')
});
所有的动作,以及像multicast
和value
这样的特殊反应,都是可链接的
他们提供的方法,返回一个新的实例的行动<action
>或反应<reactions
>改变的行为
目前,有三种链式方法:filter、pipe、while。
更多的API方法,请见官网API,好了,现在我们先了解以下链式方法:
过滤器只接受一个函数。此函数将传递更新中发出的最新值,
只有当为筛选器提供的函数返回true时,才会传递该值
action(({ update }) => {
update(1);
update(2);
update(1);
}).filter((v) => v === 1)
.start(log); // 1, 1
管道函数提供一系列功能,每个函数都提供了从UPDATE发出的最新值,并返回一个传递到链中的新值
const double = (v) => v * 2;
const px = (v) => v + 'px';
const one = just(1);
const twoPx = one.pipe(double, px);
one.start(console.log); // 1
twoPx.start(console.log); // '2px'
while
接受单个函数。此函数将传递更新中的每个值,如果函数返回false,则触发完成
just(1)
.while((v) => v === 2);
.start(console.log); // never fires, as while returned false
让我们结合pipe
和while
函数生成一个指针,该指针将其x位置作为当前视图端口的百分比输出,并在指针在屏幕上超过75%时自动停止
const pickX = ({ x }) => x;
const viewportWidth = window.innerWidth;
const percentageOfViewport = (v) => v / viewportWidth * 100;
const asPercent = (v) => v + '%';
pointer()
.pipe(pickX, percentageOfViewport) // The output of this
.while((v) => v < 75) // Gets passed to this
.pipe(asPercent) // Before being passed to thi
每个action都会返回一个stop
方法
const foo = tween().start(console.log);
foo.stop();
我们来思考下,初始化中的函数是怎么知道自己被停止了呢
每个初始化函数都可以选择性地返回一个API。这可以包括一个stop
函数
const oneEverySecond = action(({ update }) => {
const updateOne = () => update(1);
const interval = setInterval(updateOne, 1000);
return {
stop: () => clearInterval(interval)
};
});
const foo = oneEverySecond.start();
setTimeout(() => foo.stop(), 3000); // 1, 1, 1
你也可以返回自定义API
const valueEverySecond = action(({ update }) => {
let outputValue = 1;
const updateOne = () => update(outputValue);
const interval = setInterval(updateOne, 1000);
return {
stop: () => clearInterval(interval),
setOutputValue: (v) => outputValue = v
};
});
const foo = valueEverySecond.start();
foo.setOutputValue(2); // 2, 2...
通过链接操作,我们可以创建具有不同行为的新操作( action)
这种灵活性在所有动画和输入上都可用,后面的博客将涉及这些功能