# tween-fn

> A lightweight animation library which focuses on providing an expressive functional API to compose complex animations without assuming anything about your application

Latest version **1.1.7** (published 2021-06-17) · MIT license · 0 weekly downloads

## Install

```sh
npm install tween-fn
pnpm add tween-fn
yarn add tween-fn
bun add tween-fn
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.1.7 |
| Published | 2021-06-17 |
| First published | 2021-03-11 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 0 |
| Unpacked size | 32.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 0 |
| Author | Manuel Aljibes Rosas |
| Maintainers | manuelaljibesrosas |
| Keywords | animation, tween, tween-fn, motion, transition |

## Links

- npm: https://www.npmjs.com/package/tween-fn
- Repository: https://github.com/manuelaljibesrosas/tween-fn
- Homepage: https://github.com/manuelaljibesrosas/tween-fn#readme
- Issues: https://github.com/manuelaljibesrosas/tween-fn/issues
- npm.io page: https://npm.io/package/tween-fn

## Alternatives

- [@luma.gl/experimental](https://npm.io/package/@luma.gl/experimental.md) — 77.1K weekly downloads
- [persona-harness](https://npm.io/package/persona-harness.md) — 4.7K weekly downloads
- [@tsparticles/effect-bubble](https://npm.io/package/@tsparticles/effect-bubble.md) — 4.6K weekly downloads
- [f3d](https://npm.io/package/f3d.md) — 730 weekly downloads
- [spark-html-motion](https://npm.io/package/spark-html-motion.md) — 298 weekly downloads

## Recent versions

- 1.1.7 (latest) — 2021-06-17
- 1.1.6 — 2021-06-17
- 1.1.5 — 2021-06-17
- 1.0.5 — 2021-03-17
- 1.0.4 — 2021-03-17
- 1.0.3 — 2021-03-17
- 1.0.2 — 2021-03-16
- 1.0.1 — 2021-03-16
- 1.0.0 — 2021-03-11

## README

# Tween-fn

A tiny ([~1.7kb gzipped](https://bundlephobia.com/result?p=tween-fn@1.0.2)) library for coordinating __complex animation sequences__, it focuses on providing the means for writing a _description_ of a sequence using a small but powerful set of function primitives which are fully composable. Unlike existing alternatives like `GSAP` or `animejs`, `tween-fn` __does not assume anything about your application__, consequently, it doesn't know anything about the DOM or whichever JS framework you happen to prefer, this allows you to be very explicit about what you're trying to achieve

### What do we mean by complex animation sequences?

It basically boils down to two concepts: __parallel__ and __sequencial__ execution, and some of the most popular libraries available today do offer _some degree_ of support for these concepts, but they usually delegate the task of composition to the end-user in one way or another

Libraries like `react-motion` although great in their own right, seem to be optimized for __one-off__ animations (what we call `unit`s), the API for composing sequences is weird and seems to have been included retroactively

This need can arise surprisingly often, here's an example from a simple [trivia app](https://github.com/manuelaljibesrosas/trivia):

[GIF]

Notice how we needed the background to begin a looping animation right away, then the logo fades in followed by a tap icon a few milliseconds later, which also includes a looping animation to emphasize the tapping gesture. This can not only happen on macro interactions like a page transition, but also on micro interactions like a button click:

[GIF]

Here we wanted to [...], you might call both separately with a delay, but its nice to have the idea of this being a single unit be captured in the code. You could argue that the difference between orchestrating the two separately and controlling both transitions in a single unit is barely noticeable, and you'd be right, but still, we'd like to have a choice to decide wether or not it makes sense to make that consession and not have the limitations of a library to make the decision for us

## Enabling composition
One of the main pain points with existing solutions is how hard it is to organize a sequence of animations, we see this as being fundamentally an issue of _expressiveness_, and as it turns out, you can address most of these problems by making your primitives composable

```javascript
// `anime.js`'s Timeline function forces you to
// "flatten" the sequence


// composable primitives allow you to group
// sequences which are semantically related
// in a natural way
```

## Promoting abstraction
By abstracting out highly composable pieces into reusable units, you gain a good deal of readability as the _intention_ behind the sequence is embeded into the code itself:

```javascript
const master = parallel([
  backgroundShapesLoop,
  sequence([
    bannerIntro,
    parallel([
      tapIndicatorIntro,
      tapIndicatorLoop,
    ]),
  ]),
]);
```

This is the actual code for that intro sequence we showed earlier, the main takeaway from this snippet of code is that we've rescued the intention behind the animation and made it _explicit_. Taking advantage of these abstraction and composition abilities leads to code that is __easy to reason about__

## Playground
Here's a remake of a button taken from _Super Mario Party_, unsurprisingly, there's a lot that goes into making those [juicy](https://www.youtube.com/watch?v=Fy0aCDmgnxg) Nintendo animations, check the code to see what's under the hood, or read [this article](https://medium.com) which goes in-depth into the making of this button

## Closing thoughts
The web as we know it is constantly changing, and as the pool of devices that access our webapps become more and more capable, new possibilites are unlocked. Check out the [react integration](https://medium.com), for bugfixes and suggestions you can use the appropiate channels on [Github](https://github.com/manuelaljibsrosas/tween-fn).
## `react` integration
There are X things we want from a React integration [...]
## `redux` integration using a queue
Useful when you need to split a sequence in different places. This rescues the intention and makes it __explicit__

## Installation

```bash
yarn add tween-fn
```

or

```bash
npm i -S tween-fn
```

## Quickstart

We start by writing a sequence using the primitives provided by the library

```typescript
const seq = sequence([
  unit({
    duration: 250,
    change: (value) => {
      el.style.width = `${interpolate(value, 100, 150)}px`;
    },
  }),
  unit({
    duration: 500,
    ease: easings.SQUARED,
    change: (value) => {
      el.style.transform = `translateX(${interpolate(value, 0, 50)})`;
    },
  }),
]);
```

And then pass that to `run` to play it

```typescript
run(seq);
```

For cancelation, you can use the subscription object returned from `run`

```typescript
const subscription = run(seq);

// somewhere else in your application...
subscription.unsubscribe();
```

## Recipes

### Animating multiple `transform`s
To apply multiple transforms at the same time, use the `meta` object supplied to your callback functions to rescue the original value of the transform, then compute the new transformation using the `computeTransform` function

```typescript
unit({
  begin: (meta) => { meta.originalTransform = circle.style.transform; },
  change: (value, { originalTransform }) => {
    circle.style.transform = computeTransform(
      originalTransform,
      `scale(${interpolate(value, 1, 1.2)}) translateX(${interpolate(value, 0, 100)}px)`,
    );
  },
});
```

### SVG path animations
Use the `interpolatePath` utility to easily animate between two different paths

```typescript
const path1 = '';
const path2 = '';

unit({
  change: (value) => {
    path.setAttribute('d', interpolatePath(value, path1, path2));
  },
});
```

### Staggering
Given a list of items, you can coordinate a staggering animation using `mergeAll` and adding delay to each animation

```typescript
mergeAll(nodeList.map((node, i) => unit({
  delay: 100 * i, // 100 miliseconds of delay between each animation
  change: (value) => {
    // do something...
  },
})));
```

## API

### `unit`
Used to create an animation, takes the following options

```typescript
interface TweenOptions {
  iterations?: number;
  direction?: directions;
  from?: number;
  to?: number;
  delay?: number;
  duration?: number;
  ease?: easingFn;
  begin?: (meta?: object | null) => void;
  update?: (y: number, meta?: object | null) => void;
  complete?: (y: number, meta?: object | null) => void;
  change?: (y?: number, meta?: object) => void;
  loop?: (y?: number, meta?: object) => void;
  meta?: object;
}
```

### `mergeAll`
Used for parallel execution of multiple animations

```typescript
mergeAll(ts: Array<Tween>): Tween
```

### `sequence`
Describes a _sequence_ of animations, where each animation supplied will run only after the previous one has completed (unless a negative value for `delay` is used)

```typescript
sequence(ts: Array<Tween>): Tween
```

### `run`
Executes the given description

```typescript
run(tween: Tween): Subscription
```

## Utils

### `easings`
A dictionary holding common easing functions, available functions are

```typescript
easings.LINEAR
easings.SQUARED
easings.CUBIC
easings.QUART
easings.QUINT
easings.EASE_OUT_QUINT
easings.EASE_IN_OUT_QUINT
easings.EASE_OUT_ELASTIC
```

Check out [easings.net](https://easings.net) for more information regarding these

### `interpolate`
Linearly interpolates between two values

```typescript
interpolate(progress: number, start: number, end: number): number
```

### `interpolatePath`
Linearly interpolates between two paths, __paths must have the same number of points__

```typescript
interpolatePath(progress: number, p1: string, p2: string): string
```

### `computeTransform`
Replaces values defined by `source` from `target`. Returns the new transform string

```typescript
computeTransform(target: string, source: string): string

// outputs `translate(-50%, -50%) scale(1) rotate(0)`
computeTransform(
  'translate(-50%, -50%) scale(1.2) rotate(5deg)',
  'scale(1) rotate(0)'
)
```

## Roadmap
* playback controls
* more easing functions
* add examples

---
_Source: https://npm.io/package/tween-fn · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
