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

spring-easing

v2.3.3

Published

Quick and easy spring animations. Works with other animation libraries (animejs, framer motion, motion one, @okikio/animate, etc...) or the Web Animation API (WAAPI).

Downloads

1,100

Readme

spring-easing

Open Bundle

NPM | Deno | GitHub | Docs | License

Quick and easy spring animations. Works with other animation libraries (gsap, animejs, @okikio/animate, motion one, framer motion, etc...) or the Web Animation API (WAAPI), you can learn more in the Usage section.

spring-easing works by generating arrays of frame's which when placed in linear order creates a smooth spring like animation.

A frame represent a single frame of an animation

Note: the spring-easing package also supports 4 extra variants of spring, namely spring-in, spring-out, spring-out-in, and spring-in-out, you can use these easing to create some really unique spring like animations.

You can create animation's like this with spring-easing,

Check out the spring easing variants on Codepen.

Attention: This entire library is a lightweight version of the CustomEasing implemented in @okikio/animate, which supports only string and number interpolation. If you'd like the complete CustomEasing with color interpolation, complex value interpolation, and more, go through the source code as a Github Gist, which is licensed under the MIT license.

Installation

Node

npm install spring-easing
yarn add spring-easing

or

pnpm install spring-easing

Deno

import { SpringEasing } from "https://deno.land/x/spring_easing/mod.ts";

Usage

import { SpringEasing } from "spring-easing";
// or
import SpringEasing from "spring-easing";

You can also use it directly through a script tag:

<script src="https://unpkg.com/spring-easing" type="module"></script>
<script type="module">
  // You can then use it like this
  const { SpringEasing } = window.SpringEasing;
</script>

You can also use it via a CDN, e.g.

import { SpringEasing } from "https://esm.run/spring-easing";
// or
import { SpringEasing } from "https://esm.sh/spring-easing";
// or
import { SpringEasing } from "https://unpkg.com/spring-easing";
// or
import { SpringEasing } from "https://cdn.skypack.dev/spring-easing";
// or
import { SpringEasing } from "https://deno.bundlejs.com/file?q=spring-easing";
// or any number of other CDN's

Use with Animation Libraries

Note: I cannot guarantee that every animation library works with spring-easing, for example, if an animation library doesn't support array values as keyframes, it won't work well with spring-easing.

The libraries that have been tested are:

| Animation Library | Support | Demo | | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | | GSAP | ✅ Yes - Wrap Method | Codepen | | animejs | ✅ Yes - Array Keyframes | Codepen | | Framer Motion | ✅ Yes - Array Keyframes | Codepen | | Motion One | ✅ Yes - Array Keyframes | Codepen | | @okikio/animate | ✅ Yes - Array Keyframes | Codepen | | Web Animation API (WAAPI) | ✅ Yes - Array Keyframes | Codepen | | Linear Easing (CSS) | ✅ Yes - CSS Spring Easing | Codepen | | Linear Easing (WAAPI) | ✅ Yes - CSS Spring Easing | Codepen |

e.g.

import anime from "animejs";
import { SpringEasing, SpringOutFrame } from "spring-easing";

// Note: this is the return value of `SpringEasing` and `GenerateSpringFrames`
let [translateX, duration] = SpringEasing([0, 250], {
  easing: "spring-out-in(1, 100, 10, 0)",
  // You can change the size of Array for the SpringEasing function to generate
  numPoints: 200,
  // The number of decimal places to round, final values in the generated Array
  // This option doesn't exist on `GenerateSpringFrames`
  decimal: 5,
});

anime({
  targets: "div",

  // Using spring easing animate from [0 to 250] using `spring-out-in`
  translateX,

  // You can interpolate between strings
  // You can set the easing without an easing options object
  // You can interpolate between more than 2 values
  // Remember the `0` index of `SpringEasing` is an array of spring animation keyframes
  rotate: SpringEasing(
    ["0turn", 1, 0, 0.5],
    [SpringOutFrame, 1, 100, 10, 0]
  )[0],

  // TIP... Use linear easing for the proper springy effect
  easing: "linear",

  // The optimal duration for this specific spring configuration, e.g. mass, velocity, damping, etc...
  duration,
});

Or

import { CSSSpringEasing } from "spring-easing";

let [easings, duration] = CSSSpringEasing({
  easing: "spring-out-in(1, 100, 10, 0)",
  // The number of decimal places to round to
  decimal: 5,
});

document.querySelector('div').animate({
  // Using spring easing animate from [0 to 250] using `spring-out-in`
  translate: ["0", "250px"],

  // You can interpolate between strings
  // You can set the easing without an easing options object
  // You can interpolate between more than 2 values
  // Remember the `0` index of `SpringEasing` is an array of spring animation keyframes
  rotate: ["0turn", "1turn", "0turn", "0.5turn"],
}, {
  // TIP... Use linear easing for the proper springy effect
  easing: `linear(${easings})`,

  // The optimal duration for this specific spring configuration, e.g. mass, velocity, damping, etc...
  duration,
});

Note: make sure to read the comments above, as they are valuable resources for understanding what is happening.

Check out this demo on Codepen

Showcase

A couple sites/projects that use spring-easing:

API

NEW CSS Spring Easing & support for the linear() easing function

CSSSpringEasing

Generates a string that represents a set of values used with the linear-easing function to replicate spring animations, you can check out the linear-easing playground here https://linear-easing-generator.netlify.app/ Or check out a demo on Codepen https://codepen.io/okikio/pen/vYVaEXM

CSS Spring Easing has 4 properties they are easing (all spring frame functions are supported), numPoints (the size of the Array the frmae function should create), decimal (the number of decimal places of the values within said Array) and quality (how detailed/smooth the spring easing should be)..

| Properties | Default Value | | ----------- | ----------------------- | | easing | spring(1, 100, 10, 0) | | numPoints | 50 | | decimal | 3 | | quality | 0.85 |

CSSSpringEasing is meant to be used with the linear-easing() function to replicate spring animations. It is based on the work done by Jake Archibald in his Linear Easing Generator.

Note: This feature will only work on versions of browsers from ~a month ago (Chrome & Edge 113, and Firefox 112) except for Safari which doesn't support it yet.

import { CSSSpringEasing } from "spring-easing";

let [easing, duration] = CSSSpringEasing({
  easing: "spring-out-in(1, 100, 10, 0)",

  // You can change the size of Array for the SpringEasing function to generate
  numPoints: 200,

  // The number of decimal places to round, final values in the generated Array
  // This option doesn't exist on {@link GenerateSpringFrames}
  decimal: 5,

  // How detailed/smooth the spring easing should be
  // 0 means not smooth at all (shorter easing string)
  // 1 means as smooth as possible (this means the resulting easing will be a longer string)
  quality: 0.85,
});

document.querySelector("div").animate(
  {
    translate: ["0px", "250px"],
    rotate: ["0turn", "1turn", "0turn", "0.5turn"],
  },
  {
    easing: `linear(${easing})`,

    // The optimal duration for this specific spring
    duration,
  }
);

Note: You can also use custom easings with CSSSpringEasing e.g.

import {
  CSSSpringEasing,
  limit,
  registerEasingFunctions,
} from "spring-easing";

registerEasingFunctions({
  bounce: (t) => {
    let pow2: number,
      b = 4;
    while (t < ((pow2 = Math.pow(2, --b)) - 1) / 11) {}
    return (
      1 / Math.pow(4, 3 - b) - 7.5625 * Math.pow((pow2 * 3 - 2) / 22 - t, 2)
    );
  },
  elastic: (t, params: number[] = []) => {
    let [amplitude = 1, period = 0.5] = params;
    const a = limit(amplitude, 1, 10);
    const p = limit(period, 0.1, 2);
    if (t === 0 || t === 1) return t;
    return (
      -a *
      Math.pow(2, 10 * (t - 1)) *
      Math.sin(
        ((t - 1 - (p / (Math.PI * 2)) * Math.asin(1 / a)) * (Math.PI * 2)) / p
      )
    );
  },
});

CSSSpringEasing("bounce"); // ["0, 0.013, 0.015, 0.006 8.1%, 0.046 13.5%, 0.06, 0.062, 0.054, 0.034, 0.003 27%, 0.122, 0.206 37.8%, 0.232, 0.246, 0.25, 0.242, 0.224, 0.194, 0.153 56.8%, 0.039 62.2%, 0.066 64.9%, 0.448 73%, 0.646, 0.801 83.8%, 0.862 86.5%, 0.95 91.9%, 0.978, 0.994, 1", ...]
CSSSpringEasing("elastic(1, 0.5)"); // ["0, -0.005 32.4%, 0.006 40.5%, 0.034 51.4%, 0.033 56.8%, 0.022, 0.003, -0.026 64.9%, -0.185 75.7%, -0.204, -0.195, -0.146, -0.05, 0.1 89.2%, 1", ...]

getOptimizedPoints

This function generates an optimized set of points to be used with the linear-easing() function using the Ramer-Douglas-Peucker algorithm and rounds the x and y values of the resulting points.

import { getOptimizedPoints } from "spring-easing";

const points = [
  [0, 0],
  [0.1, 0.2],
  [0.5, 1],
  [0.9, 0.2],
  [1, 0],
];
const round = 2;
const simplify = 0.1;

console.log(getOptimizedPoints(points, simplify, round)); //= [[0, 0], [0.5, 1], [1, 0]]

getLinearSyntax

This function converts a given set of points into an array of strings in a this format ["value percent%", ...] e.g. ["0", "0.25 13.8%", "0.6 45.6%", "0.1", "0.4 60%", ...]. It's used to generate the syntax for the linear-easing function.

import { getLinearSyntax } from "spring-easing";

const points = [
  [0, 0],
  [0.1, 0.2],
  [0.5, 1],
  [0.9, 0.2],
  [1, 0],
];
const round = 2;

console.log(getLinearSyntax(points, round)); //= [ '0', '0.2 10%', '1', '0.2 90%', '0' ]

RE-INSTATED Added batch version of SpringEasing and the Interpolation functions which use a new syntax.

The other version of spring-easing interpolation functions follow this syntax (t, values, decimal) => string | number | any, batch interpolation function use this new syntax (arr_t, values, decimal) => string[] | number[] | any[].

The key difference between both syntaxes are the parameters each function takes and the return value of each function.

The older syntax returned instantaneous frame values at a specific t-value, but the new syntax returns all the frames that make the entire animation, allowing for performance optimizations that couldn't be done before.

For the most part this shouldn't leave too much of an effect, but for those high-perf. applications this new batch synatax should prove useful. e.g.

function batchInterpolateNumber(
  arr_t: number[],
  values: number[],
  decimal = 3
) {
  // nth index
  const n = values.length - 1;

  return arr_t.map((t) => {
    // The current index given t
    const i = limit(Math.floor(t * n), 0, n - 1);

    const start = values[i];
    const end = values[i + 1];
    const progress = (t - i / n) * n;

    return toFixed(scale(progress, start, end), decimal);
  });
}

BatchSpringEasing([0, 250], `spring`, batchInterpolateNumber);

RE-INSTATED There is a new toAnimationFrames function that converts interpolation functions written in this style (t, values, decimal) => { ... } to work in BatchSpringEasing. e.g.

import {
  BatchSpringEasing,
  toAnimationFrames,
  toFixed,
  scale,
  limit,
} from "spring-easing";

function interpolateNumber(t: number, values: number[], decimal = 3) {
  // nth index
  const n = values.length - 1;

  // The current index given t
  const i = limit(Math.floor(t * n), 0, n - 1);

  const start = values[i];
  const end = values[i + 1];
  const progress = (t - i / n) * n;

  return toFixed(scale(progress, start, end), decimal);
}

function interpolatePixels(t: number, values: number[], decimal = 3) {
  const result = interpolateNumber(t, values, decimal);
  return `${result}px`;
}

BatchSpringEasing([0, 250], "spring", toAnimationFrames(interpolatePixels));

NEW Optimized perf. of spring generation w/ help from @jakearchibald

NEW mass, stiffness, damping, and velocity now have a smaller minimum limit of 0.0001 instead of 0.1

REVERT The new interpolation syntax has been reverted and removed; instantNumber, etc... functions have been renamed to interpolateNumber, etc...

NEW Re-introduced instantaneous interpolation functions. e.g.

import {
  interpolateNumber,
  interpolateString,
  interpolateSequence,
  interpolateComplex,
} from "spring-easing";

These functions represent the interpolated value at a specific instance in time, where time is represented by t with a range of 0 to 1. You can use these functions as building blocks to create your own custom interpolation functions.

NEW (deprecated) interpolateUsingIndex is now an alias of interpolateSequence, it still keeps the same functionality. The recommendation is to use interpolateSequence instead of interpolateUsingIndex, but you can still keep using interpolateUsingIndex, but beware it can be removed in future versions.

NEW Re-introduced instantaneous interpolation functions. e.g.

import {
  instantNumber,
  instantString,
  instantSequence,
  instanceComplex,
} from "spring-easing";

These functions represent the interpolated value at a specific instance in time, where time is represented by t with a range of 0 to 1. You can use these functions as building blocks to create your own custom interpolation functions.

BREAKING CHANGE Interpolation functions use a new syntax.

In older versions of spring-easing interpolation functions used to follow a syntax called the instantaneous interpolation function (t, values, decimal) => string | number | any, the new syntax is called interpolation function (frames, values, decimal) => string[] | number[] | any[].

The key difference between both syntaxes are the parameters each function takes and the return value of each function.

The older syntax returned instantaneous frame values at a specific t-value, but the new syntax returns all the frames that make the entire animation, allowing for performance optimizations that couldn't be done before.

For the most part this shouldn't leave too much of an effect as all the built-in interpolation functions have been updated to use the new synatax. e.g.

function interpolateNumber(frames: number[], values: number[], decimal = 3) {
  // nth index
  const n = values.length - 1;

  return frames.map((t) => {
    // The current index given t
    const i = limit(Math.floor(t * n), 0, n - 1);

    const start = values[i];
    const end = values[i + 1];
    const progress = (t - i / n) * n;

    return toFixed(scale(progress, start, end), decimal);
  });
}

SpringEasing([0, 250], `spring`, interpolateNumber);

NEW There is a new toAnimationFrames function that be used on instantaneous interpolation functions, to transform them into complete animation interpolation functions. e.g.

import {
  SpringEasing,
  toAnimationFrames,
  toFixed,
  scale,
  limit,
} from "spring-easing";

function interpolateNumber(t: number, values: number[], decimal = 3) {
  // nth index
  const n = values.length - 1;

  // The current index given t
  const i = limit(Math.floor(t * n), 0, n - 1);

  const start = values[i];
  const end = values[i + 1];
  const progress = (t - i / n) * n;

  return toFixed(scale(progress, start, end), decimal);
}

function interpolatePixels(t: number, values: number[], decimal = 3) {
  const result = interpolateNumber(t, values, decimal);
  return `${result}px`;
}

SpringEasing([0, 250], "spring", toAnimationFrames(interpolatePixels));

NEW Easily register new easing functions. e.g.

import { SpringEasing, registerEasingFunction } from "spring-easing";

registerEasingFunction("linear", (t) => t);
registerEasingFunctions({
  quad: (t) => Math.pow(t, 2),
  cubic: (t) => Math.pow(t, 3),
});

SpringEasing([0, 250], "linear");

SpringEasing([0, 250], "quad");

NEW SpringEasing now support interpolating between strings. It treats the units of the first value as the units for the rest of the values to interpolate between. e.g.

SpringEasing(["0turn", "1px", "18rem", "125deg", 25], ...)

Important All the values above get transformed to ["0turn", "1turn", "18turn", "125turn", "25turn"], before being interpolated.

NEW interpolateStrings, interpolateUsingIndex, and interpolateComplex, are now built-in, they allow for supporting string keyframes.

NEW Custom interpolation functions are now supported. e.g.

import { interpolateNumber, toFixed, scale, limit } from "spring-easing";
// ...
export function interpolateColor(t: number, values: string[], decimal = 3) {
  const color = transpose(...values.map((v) => rgba(v))).map(
    (colors: number[], i) => {
      const result = interpolateNumber(t, colors);
      return i < 3 ? Math.round(result) : toFixed(result, decimal);
    }
  );

  return `rgba(${color.join()})`;
}

SpringEasing(["red", "green", "#4f4"], "spring", interpolateColor);

Important The logic for color interpolation is defined in this tests/utils/interpolate-color.ts.

The API of spring-easing is pretty straight forward, the SpringEasing function generates an array of values using a frame functions, which in turn creates the effect of spring easing.

To use this properly make sure to set the easing animation option to "linear". Check out a demo of SpringEasing at https://codepen.io/okikio/pen/MWEdzNg

SpringEasing has 3 properties they are easing (all the easings from EasingFunctions are supported on top of frame functions like SpringFrame, SpringFrameOut, etc..), numPoints (the size of the Array the frame function should create), and decimal (the number of decimal places of the values within said Array).

| Properties | Default Value | | ----------- | ----------------------- | | easing | spring(1, 100, 10, 0) | | numPoints | 50 | | decimal | 3 |

By default, Spring Easing support easings in the form,

| constant | accelerate | decelerate | accelerate-decelerate | decelerate-accelerate | | :------- | :----------------- | :--------- | :-------------------- | :-------------------- | | | spring / spring-in | spring-out | spring-in-out | spring-out-in |

All Spring easing's can be configured using theses parameters,

spring-*(mass, stiffness, damping, velocity)

Each parameter comes with these defaults

| Parameter | Default Value | | --------- | ------------- | | mass | 1 | | stiffness | 100 | | damping | 10 | | velocity | 0 |

To understand what each of the parameters of SpringEasing mean and how they work I suggest looking through the SpringEasing API Documentation

Note: the return value of the SpringEasing function is actually [Array of keyframes, duration], in that order.

For a full understanding of what is happening in the library, pleace check out the API site for detailed API documentation.

Browser Support

| Chrome | Edge | Firefox | Safari | IE | | ------ | ---- | ------- | ------ | --- | | 4+ | 12+ | 4+ | 4+ | 10+ |

Native support for spring-easing is great as it doesn't use any browser specific or nodejs specific API's, you should be good to use spring-easing in any environment.

Note: CSSSpringEasing is meant for browsers which have support for the linear() easing function, which as of right now is Chrome & Edge 113 + Firefox 112, Safari doesn't support it yet.

Contributing

I encourage you to use pnpm to contribute to this repo, but you can also use yarn or npm if you prefer.

Install all necessary packages

npm install

Then run tests

npm test

Build project

npm run build

Preview API Docs

npm run typedoc && npm run preview

Note: this project uses Conventional Commits standard for commits, so, please format your commits using the rules it sets out.

License

See the LICENSE file for license rights and limitations (MIT).

The CSSSpringEasing, getOptimizedPoints and getLinearSyntax function are based of the work done by Jake Archibald in his Linear Easing Generator and are thus licensed under the Apache License 2.0.