当前位置: 首页 > 工具软件 > Popmotion > 使用案例 >

Popmotion轻量级动画(一)

商璞
2023-12-01

导言

Popmotion是一款轻量级动画库,它在2017年火得一塌糊涂,so,开始我们的popmotion之旅吧,具体动画体验可以去官网demo,这里主要讲述popmotion方法和原理

Actions and reactions<行为操作和反应>

新手教程里面说到的tween方法,是作为一个action返回的

Popmotion是一个响应式的组件库,并且action是一个创建流的函数,这些action输出到一个reactions

在Popmotion中每个动画和输入都是一个action,在此博客中,我们将用实例讲解什么是action和reactions

Import

import { action } from ‘popmotion’;

创建一个action

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!')
});

所有的动作,以及像multicastvalue这样的特殊反应,都是可链接的
他们提供的方法,返回一个新的实例的行动<action>或反应<reactions>改变的行为

目前,有三种链式方法:filter、pipe、while。
更多的API方法,请见官网API,好了,现在我们先了解以下链式方法:

filter

过滤器只接受一个函数。此函数将传递更新中发出的最新值,
只有当为筛选器提供的函数返回true时,才会传递该值

action(({ update }) => {
  update(1);
  update(2);
  update(1);
}).filter((v) => v === 1)
  .start(log); // 1, 1

pipe

管道函数提供一系列功能,每个函数都提供了从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

while接受单个函数。此函数将传递更新中的每个值,如果函数返回false,则触发完成

just(1)
  .while((v) => v === 2);
  .start(console.log); // never fires, as while returned false

综合链式方法

让我们结合pipewhile函数生成一个指针,该指针将其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

每个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

你也可以返回自定义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)

  • 这种灵活性在所有动画和输入上都可用,后面的博客将涉及这些功能

  • 在下一篇博客中,我们将学习如何使用两个输入操作(指针和侦听)来实现指针跟踪
 类似资料: