npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mutate-animate

v1.4.2

Published

A highly customized animation library

Downloads

17

Readme

动画库 mutate-animate 使用说明

AnimationBase

这是这个动画库的基础,是一个抽象类,库中包含的两个动画类 Animation 和 Transition 都是继承于它。

下面我们先对一些概念进行介绍

速率曲线

这个动画的核心是速率曲线。虽说叫做速率曲线,但其实是完成度曲线,它是一个函数,输入一个[0,1]间的数值,输出一个[0,1]间的数值(当然也可以超过这个区间,但一般来讲不要超过)。输入值表示了动画在时间上的完成度,输出值,也就是函数的返回值表示了动画在过程上的完成度。

该库中内置了丰富的速率曲线生成函数,这些函数可以根据输入的参数输出对应的速率曲线函数,你可以直接从该库中引入。包括以下内容:

  1. 线性渐变函数 linear(),该函数返回一个线性变化函数

  2. 三角渐变函数 trigo(type: 'sin' | 'sec', mode: EaseMode),该函数返回一个指定属性的三角函数变化函数,其中EaseMode可以填in out in-out center,分别表示 慢-快 快-慢 慢-快-慢 快-慢-快

  3. 幂函数渐变 power(n: number, mode: EaseMode),该函数返回一个以 x^n 变化的函数,n 是指数

  4. 双曲渐变函数 hyper(type: 'sin' | 'tan' | 'sec', mode: EaseMode),该函数返回一个双曲函数,分别是 双曲正弦 双曲正切 双曲正割

  5. 反三角渐变函数 inverseTrigo(type: 'sin' | 'tan', mode: EaseMode),该函数返回一个反三角函数

  6. 贝塞尔曲线渐变函数 bezier(...cps),参数为贝塞尔曲线的控制点纵坐标(横坐标不能自定义,因为一个时刻不能对应多个速率)示例:bezier(0.4, 0.2, 0.7); // 三个控制点的四次贝塞尔曲线渐变函数

  7. 震动变化函数 shake(power: number, timing: TimingFn),它可以输出一个来回震动的速率函数,其中power是可能的最大震动量,timing是震动量的变化函数

当然,你也可以自定义速率曲线,只要满足(input: number) => number即可

路径曲线

除了速率曲线,库还提供了一个路径曲线,它允许xy在一定的路径下移动。它与速率曲线类似,只不过输出值变成了[x,y]数组。库内置了两种路径曲线,它们在mutate.path中,包括:

  1. 圆形运动 circle(r: number, n?: number, center?: Point, start?: number, timing?: TimingFn, inverse: boolean)r是圆的半径,n是圈数,center是圆心坐标,start是起始角度,timing描述半径大小的变化,inverse说明了是否翻转timing函数,后面五个参数可以不填

  2. 贝塞尔曲线 bezierPath(start: Point, end: Point, ...cps: Point[]),其中startend是起点和结束点,应当填入[x, y]数组,cps是控制点,也是[x, y]数组,示例:bezierPath([0, 0], [200, 200], [100, 50], [300, 150], [200, 180]),这是一个起点为[0, 0],终点为[200, 200],有三个控制点的四次贝塞尔曲线

Animation

这是一个非常强大的类,你可以用它来动画改变任何数值

引入 & 创建

import { Animation } from 'mutate-animate';

const ani = new Animation();

动画属性

首先,说到动画,就一定会有一个作用属性。Animation自身提供了 4 个系统自带的属性,分别是x y angle size,它们分别表示横坐标、纵坐标、旋转角度、放缩大小。当你创建了一个Animation实例(这里命名为ani)后,你可以通过ani.x ani.y ani.angle ani.size来获取,注意这些属性理论上是只读的

你还可以自定义动画属性,你可以通过调用ani.register(key: string, initValue: number)来注册一个新的动画属性,注意注册后的属性会被保存在ani.value中。其中key是属性名称,可以与系统属性重名,initValue是属性的初始值

动画设置

为了让动画能够正确运行,需要对动画进行设置,包括时间、相对模式等,请看以下示例

ani.time(1000) // 设置动画时间,默认为0,不过注意当时间为0时执行动画仍需要1帧的时间
    .absolute() // 设置为绝对模式,相对模式为relative,默认为绝对模式,注意相对模式下属性为相加关系
    .mode(linear()); // 设置速率曲线,默认为线性变化

如果是坐标震动变化的话,请使用ani.mode(shake(...), true)

如果不是关于坐标的震动变化,请依然使用ani.mode(shake(...))

运行动画

以上准备工作做完之后,我们就可以运行动画了,请看以下示例

ani.move(100, 100); // 执行运动到100,100的动画

没错!运行动画就是这么简单!这样的话它的坐标就会按照指定的方式运动了。引擎本身提供了五种动画方式,分别是:

  1. move(x: number, y: number)运动到某个点
  2. scale(size: number)放缩
  3. rotate(angle: number)旋转
  4. moveAs(path: PathFn)按照路径函数移动,使用该动画时请先将相对模式改为相对
  5. shake(x: number, y: number)震动,x 和 y 分别表示横纵坐标上的震动大小,1 表示最大,0 表示最小

当然,还有运行自定义属性的动画的函数,它是ani.apply(key: string, n: number),其中key是动画属性,n是动画的目标值

等待

很多时候我们需要等待某个动画执行完毕,当然这个引擎也提供了这个功能,共有三种等待方式

  1. ani.n(n: number),等待 n 个动画执行完毕
  2. ani.w(type: string),等待某种动画执行完毕
  3. ani.all(),等待所用动画执行完毕

他们都是async function,所以请使用awaitpromise来执行等待

除此之外,库中还提供了一个等待函数sleep(time: number),它允许你等待指定毫秒数,同样也是async function

绑定

你还可以绑定多个动画属性,让他们可以在同一个渐变函数的作用下变化。你可以使用 ani.bind(...attr)来绑定。绑定之后,这三个动画属性可以被一个返回了长度为 3 的数组的渐变函数执行。绑定使用 ani.bind,设置渐变函数仍然使用 ani.mode,注意它与单个动画属性是分开的,也就是它不会影响正常的渐变函数。然后使用 ani.applyMulti 即可执行动画

// 自定义的一个三属性渐变函数
function b(input) {
    return [input * 100, input ** 2 * 100, input ** 3 * 100];
}
ani.bind('a', 'b', 'c') // 这样会绑定abc这三个动画属性
    .mode(b) // 自定义的一个返回了长度为3的数组的函数
    .time(5000)
    .absolute()
    .applyMulti(); // 执行这个动画

监听

你可以使用ani.listen(type: string, fn: (e: Animation) => void)来监听动画信息,由于不常用,这里便不再赘述。

绘制

通过上述描述,你会发现这个动画引擎并不参与绘制,因此绘制需要单独完成,你可以通过ani.ticker.add(fn: (time: number) => void)来添加绘制函数,其中time是自从这个动画被创建之后经过的毫秒数

自定义时间获取函数

你可以修改ani.getTime来修改类的时间获取函数,从而使得你可以控制动画的运行速度,甚至是暂停等。例如我想让动画的运行速度变为一半,可以写ani.getTime = () => Date.now() / 2

Transition

这是一个渐变类,允许你在设置一个数值之后让这个数值慢慢变化,类似于 css 的 transition

引入 & 创建

import { Transition } from 'mutate-animate';

const tran = new Transition();

渐变设置

与动画几乎完全一样,你依然可以使用mode() time() absolute() relative()进行渐变设置,唯一的不同是mode没有第二个参数

初始化渐变属性

你应当在第一次对渐变属性赋值时进行初始化,例如:

tran.value.x = 400;
tran.value.y = 400;

这样,x y就被初始化为了 400

执行渐变

相比于动画,渐变的执行更为简单,你只需要操作tran.value[key]即可

tran.value.x = 600; // 横坐标移至600
tran.relative();
tran.value.y = 200; // 纵坐标向下移动200,因为这里设置成了相对模式,因此赋值操作会让y增加200

自定义时间获取函数

与动画类似,你可以修改tran.getTime来修改类的时间获取函数,从而使得你可以控制渐变的运行速度,甚至是暂停等。

示例

Animation

const canvas = document.getElementById('animate-canvas') as HTMLCanvasElement;
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;

const ani = new Animation();
ctx.save();

ani.register('alpha', 1);

ani.ticker.add(() => {
    ctx.beginPath();
    ctx.restore();
    ctx.save();
    ctx.clearRect(0, 0, 800, 800);
    ctx.fillStyle = '#0ff';
    ctx.globalAlpha = ani.value.alpha;
    ctx.arc(ani.x, ani.y, 50, 0, Math.PI * 2);
    ctx.fill();
});

ani.mode(hyper('sin', 'in-out'))
    .time(2000)
    .absolute()
    .move(400, 400)
    .relative()
    .time(3000)
    .apply('alpha', -1);

await sleep(3000);
ani.time(500)
    .absolute()
    .apply('alpha', 1)
    .time(2000)
    .relative()
    .moveAs(bezier([0, 0], [200, 200], [100, 100], [150, 100]));

Transition

const canvas = document.getElementById('animate-canvas') as HTMLCanvasElement;
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;

const tran = new Transition();
ctx.save();

tran.value.x = 400;
tran.value.y = 400;

tran.ticker.add(() => {
    ctx.beginPath();
    ctx.restore();
    ctx.save();
    ctx.clearRect(0, 0, 800, 800);
    ctx.fillStyle = '#0ff';
    ctx.arc(tran.value.x, tran.value.y, 50, 0, Math.PI * 2);
    ctx.fill();
});

await sleep(1000);
tran.mode(hyper('sin', 'out')).time(600).absolute();
tran.value.x = 200;
tran.value.y = 200;
await sleep(200);
tran.value.y = 600;
await sleep(500);
tran.value.x = 400;
tran.time(2000);
await tran.all();
tran.value.x = 800;
tran.value.x = 0;
await sleep(700);
tran.value.y = 200;