{"id":17133274,"url":"https://github.com/gilbox/react-imation","last_synced_at":"2025-04-09T15:05:25.118Z","repository":{"id":78534047,"uuid":"42794934","full_name":"gilbox/react-imation","owner":"gilbox","description":"functional tweening and timeline animation for react","archived":false,"fork":false,"pushed_at":"2018-11-21T00:45:19.000Z","size":4519,"stargazers_count":236,"open_issues_count":0,"forks_count":11,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-09T15:05:21.214Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gilbox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2015-09-20T00:44:44.000Z","updated_at":"2024-08-02T20:11:54.000Z","dependencies_parsed_at":"2023-05-21T16:15:19.839Z","dependency_job_id":null,"html_url":"https://github.com/gilbox/react-imation","commit_stats":{"total_commits":191,"total_committers":6,"mean_commits":"31.833333333333332","dds":0.09947643979057597,"last_synced_commit":"6b29577a10f28812934cad607cc5b8f433c107eb"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gilbox%2Freact-imation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gilbox%2Freact-imation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gilbox%2Freact-imation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gilbox%2Freact-imation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gilbox","download_url":"https://codeload.github.com/gilbox/react-imation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248055284,"owners_count":21040157,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T19:41:48.270Z","updated_at":"2025-04-09T15:05:25.086Z","avatar_url":"https://github.com/gilbox.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-imation\n\nThis library provides\nvarious composable utilities for creating complex timeline-based animation in a react-y component-driven fashion.\n\n    npm install react-imation --save\n\nSince this is a library of composable utility functions and components that mostly\ndon't rely on each other, there is no fully bundled import. This keeps `react-imation`\nlight-weight. The following imports are available:\n\n  - `react-imation`\n  - `react-imation/animationFrame`\n  - `react-imation/Interval`\n  - `react-imation/timeline`\n  - `react-imation/tween-value-factories`\n\nFor react-native the following imports are available\n(support is limited to a subset of the above, atm):\n\n  - `react-imation/native`\n  - `react-imation/timeline/native`\n  - `react-imation/tween-value-factories`\n\n## Demos\n\n-  [Demo1](http://gilbox.github.io/react-imation/examples/demo1/demo.html): [[source](https://github.com/gilbox/react-imation/blob/master/examples/demo1/Game.js)] `tween`, `\u003cTimeline /\u003e`  ***Exploding Snowflakes***\n-  [Demo2](http://gilbox.github.io/react-imation/examples/demo2/demo.html): [[source](https://github.com/gilbox/react-imation/blob/master/examples/demo2/app.js)] `tween`, `\u003cTimeline /\u003e`, `react-motion`\n- [Demo3](http://gilbox.github.io/react-imation/examples/demo3/demo.html): [[source](https://github.com/gilbox/react-imation/blob/master/examples/demo3/RollButton.js)] `tween`, `\u003cTimeline /\u003e`, `Timeliner`\n- [Demo4](http://gilbox.github.io/react-imation/examples/demo4/demo.html): [[source](https://github.com/gilbox/react-imation/blob/master/examples/demo4/Demo4.js)] `tween`, `\u003cTimeline /\u003e`\n- [Demo5](http://gilbox.github.io/react-imation/examples/demo5/demo.html): [[source](https://github.com/gilbox/react-imation/blob/master/examples/demo5/Demo5.js)] `tween`, `\u003cTimeline /\u003e`, `ease`\n\nAlso check out [`react-track`](https://github.com/gilbox/react-track)'s'\ndemo which combines `react-imation` tweening with\nDOM tracking.\n\nIf you clone this repo you can run the demos locally via:\n\n        npm install\n        npm run examples\n\n#### In the Wild\n\n- [airbnb javascript](http://airbnb.io/projects/javascript/)\n- Your demo? ([edit this file](https://github.com/gilbox/react-imation/edit/master/README.md))\n\n## [`tween(currentFrame, keyframes, [ease])`](https://github.com/gilbox/react-imation/blob/master/src/tween.js)\n\nThe first argument, `currentFrame` is a number representing the current\nposition in the animation **timeline**.\n\nThe aforementioned **timeline** is represented by the `keyframes`\nargument which is an array of `[key, value]` touples.\nThe 2 components of each touple represents a timeline\nposition and it's state, respectively.\nNote that `tween` assumes that the keyframes are sorted.\n\n```jsx\nimport {tween} from 'react-imation';\nimport {rotate} from 'react-imation/tween-value-factories';\n\n// ...render:\n  \u003ch2\n    style=tween(time, [\n      [  0, { transform: rotate(0) } ],\n      [ 60, { transform: rotate(360) } ]\n    ])\n  \u003e\n    spin\n  \u003c/h2\u003e\n```\n\n*Note: Support for object typed `keyframes` param\nhas been removed as of `react-imation@0.5.0`*\n\nTweening values that require special formatting is\nsuper-easy. All you have to do is create a new\ntween value factory. Check out\n[`tween-value-factories.js`](https://github.com/gilbox/react-imation/blob/master/src/tween-value-factories.js)\nand you'll see what I mean.\n\n#### `tween`: tweening numbers\n\nWhile `tween` works with more sophisticated *wrapped* values as demonstrated\nabove, it also works with regular numbers. Here are some examples:\n\n    tween(0.5, [[0, 10], [1, 20]]); //=\u003e 15\n\n    tween(5, [[0, 10], [10, 20]]);  //=\u003e 15\n\n    tween(10, [[0,  0 ],\n               [20, 10],\n               [30, 20]]);       //=\u003e 5\n\n    tween(5, [[0,10], [5,0]]);    //=\u003e 5\n\nYou can use this approach to tween styles:\n\n```jsx\n\u003ch2 style={{ transform: `rotate(${tween(time, [[0, 0],[60, 360]])}deg)` }}\u003e\n  spin\n\u003c/h2\u003e\n```\n\nYou can tween all of your styles this way and it will work fine.\nHowever, when you have a lot of styles this can get tedious and difficult\nto read. For this reason, `tween` supports using *wrapped values*.\nRead the next section about creating *wrapped values* using\ntween value factories (TvFs).\n\n#### `tween`: creating wrapped values with tween value factories (TvFs)\n\nWrapped values represent complex values which we ultimately need\nto convert to strings in order to generate CSS values. We can\ncreate wrapped values easily with tween value factories.\n\nHere are the two most complex value factories:\n\n    import {combine, ease} from 'react-imation';\n\nHere are some simple value factories:\n\n    import {rotate, turn, px, translateX} from 'react-imation/tween-value-factories';\n\nI call these tween value factories *simple* because they are extremely easy to create.\nTo create a simple tween value factory first import the `createTweenValueFactory`\nfunction\n\n    import {createTweenValueFactory} from 'react-imation';\n\nand then use it like this:\n\n    const px = createTweenValueFactory(value =\u003e `${value}px`);\n    const translate3d = createTweenValueFactory(value =\u003e `translate3d(${value.join(',')})`, px);\n\nnow the value of `translate3d` is a function which can create *wrapped values*.\nFor example,\n\n    const t = translate3d(100, 50, 80); // instantiate a wrapped value `t`\n    t.resolveValue();   //=\u003e \"translate3d(100px,50px,80px)\"\n\nnote that calling `resolveValue` on the wrapped value `t` returns it's\nstring representation. You will never have to do this explicitly because\nthe `tween` function does it for you.\n\nConsider `translate3d` again\n\n    const px = createTweenValueFactory(value =\u003e `${value}px`);\n    const translate3d = createTweenValueFactory(value =\u003e `translate3d(${value.join(',')})`, px);\n\nNotice that we are passing the tween value factory `px` as the second\nargument of `createTweenValueFactory`. This tells `createTweenValueFactory`\nto create a value factory that automatically wraps each of its arguments\nwhich are *plain numbers* utilizing another\nvalue factory (`px`) before passing it into it's own value factory.\n\nConsider the TVF `percent`\n\n    const percent = createTweenValueFactory(value =\u003e `${value}%`);\n\nWe can use this with the `translate3d` TvF\n\n    const t = translate3d(percent(50), percent(20), 200);\n    t.resolveValue();    //=\u003e \"translate3d(50%,20%,200px)\"\n\nNote that since we did not wrap the third argument in a TvF,\nit was wrapped automatically by the `px` TvF and that is why\ncalling `t.resolveValue()` produced `200px` for the third argument.\n\n#### `tween`: tweening wrapped values\n\nThe real power and elegance of the `tween` function becomes apparent\nwhen you use it with TvFs (that produce wrapped values).\nOne of the primary goals of react-imation is to create a highly\nreadable and intuitive API for animation.\n\n\n    const t = tween(30, [ [ 0, rotate(0)  ],\n                          [60, rotate(360)] ])\n\n    t.resolveValue();   //=\u003e \"rotate(180deg)\"\n\nIn react we can use this in a style tag:\n\n```jsx\n\u003cdiv\n  style={{\n    backgroundColor: 'red'\n    transform: tween(time, [ [ 0, rotate(0)  ],\n                             [60, rotate(360)] ])\n  }}\u003e\n```\n\n#### `tween`: tweening object literals\n\nTweening object literals means that we are\nactually tweening the values within those objects and returning\na new object with a similar shape. This works\nwith both numbers and wrapped values.\n\n```jsx\n\u003ch2\n  style={tween(time, [\n    [ 0, { transform: rotate(0)   }],\n    [60, { transform: rotate(360) }]\n  ])}\n\u003e\n  spin\n\u003c/h2\u003e\n```\n\nThe result is something like this:\n\n```jsx\n\u003ch2 style={{ transform: 'rotate(180deg)' }}\u003e\n  spin\n\u003c/h2\u003e\n```\n\nThe real advantage of using object literals is\nthat it allows you to tween multiple style properties\nin one `tween()`:\n\n```jsx\n\u003ch2\n  style={tween(time, [\n    [  0, { backgroundColor: rgba(0,200,0,.5), transform: rotate(0)  } ],\n    [ 60, { backgroundColor: rgba(200,0,0,1), transform: rotate(360) } ]\n  ])}\n\u003e\n  spin\n\u003c/h2\u003e\n```\n\nthe result is something like:\n\n```jsx\n\u003ch2 style={{ backgroundColor: 'rgba(100,100,0,.75)',\n             transform: 'rotate(180deg)' }}\u003e\n  spin\n\u003c/h2\u003e\n```\n\n**warning**: All keyframes in a single tween must have exactly the same\nproperties. The only exception to this is when using easing.\n\n#### `tween`: easing\n\nAn easing function is a function that accepts a single argument,\n`time` and returns `time`. There are many libraries out there already\nthat provide easing functions, or you can write your own. The one\nI've been using is `functional-easing`.\n\nThere are three ways to ease with `tween`:\n\n1. Pass the easing function as the third argument to `tween`.\n2. When tweening a plain object, add an `ease` property.\n   The easing will apply to all properties in the keyframe.\n    For example:\n\n        import {Easer} from 'functional-easing';\n        const easeOutSine = new Easer().using('out-sine');\n\n        \u003ch2\n          style={tween(time, [\n            [ 0, { transform: rotate(0), ease: easeOutSine }],\n            [60, { transform: rotate(360) } ]\n          ])}\n        \u003e\n          spin\n        \u003c/h2\u003e\n\n3. Wrap a TvF in the `ease` TvF. The `ease` TvF will override\n    any other type of easing.\n\n        \u003ch2\n          style={tween(time, [\n            [ 0, { transform: ease(easeOutSine, rotate(0)) }],\n            [60, { transform: rotate(360) } ]\n          ])}\n        \u003e\n          spin\n        \u003c/h2\u003e\n\n  **Heads-up: Doing `rotate(ease(easeOutSine, 0))`\n  instead of `ease(easeOutSine, rotate(0))` unfortunately\n  does *not* work.**\n\n  Note that we did not wrap `rotate(360)` with `ease()`. Wrapping the\n  destination value is optional because the source's easing function\n  is always the one that `tween` applies.\n\n  The `ease()` TvF is automatically curried, so we can also use\n  it like this:\n\n        const easeOutSine = ease(new Easer().using('out-sine'));\n\n        \u003ch2\n          style={tween(time, [\n            [ 0, { transform: easeOutSine(rotate(0)) }],\n            [60, { transform: rotate(360) }]\n          ])}\n        \u003e\n          spin\n        \u003c/h2\u003e\n\n  **Heads-up: Doing `rotate(easeOutSine(0))` instead of\n  `easeOutSine(rotate(0))` unfortunately\n  does *not* work.**\n\n#### `tween`: combine TvF\n\n`combine` works as you might expect.\n\n    combine(rotate(90), translateX(100))\n      .resolveValue();    //=\u003e \"rotate(90deg) translateX(100px)\"\n\n\n## `\u003cInterval /\u003e`\n\n    import Interval from 'react-imation/Interval';\n\nStateless component providing an\neasy way to repeatedly set an interval.\nIt extracts away the react lifecycle challenges\nso that all you have to think about is what to do\nevery tick and how to schedule the next interval.\n\n    \u003cInterval onTick={scheduleTick =\u003e {\n      console.log('tick!');\n      scheduleTick(1000); // schedule next tick for 1 second from now\n    }} /\u003e;\n\n## `animationFrame`\n\n    import { animationFrame } from 'react-imation/animationFrame';\n\nStateless ticking decorator that manages destroying\nrequestAnimationFrame when component unmounts.\nAll you have to supply is the only argument,\na `callback` function\nwhich gets called every tick.\n\n**ES7 Decorator:** (with class-based component)\n\n    @animationFrame(({onTick}) =\u003e onTick())\n    class Foo extends Component {\n      render() {\n        return \u003cdiv\u003e{this.props.foo}\u003c/div\u003e\n      }\n    }\n\n**Functionally:** (with stateless component)\n\n    animationFrame(\n      ({onTick}) =\u003e onTick()\n    )(\n      props =\u003e \u003cdiv\u003e{props.foo}\u003c/div\u003e\n    )\n\nIn both examples above we assume an `onTick` prop\nis being passed down and it will handle each\ntick event.\n\n## `\u003cAnimationFrame /\u003e`\n\n    import { AnimationFrame } from 'react-imation/animationFrame';\n\nStateless ticking component. Just supply a callback\nfunction to `onTick` prop.\n\n    \u003cAnimationFrame onTick={() =\u003e console.log('tick'))}\u003e\n\n\n## [`\u003cTimeline /\u003e`](https://github.com/gilbox/react-imation/blob/master/src/timeline/timeline.js)\n\n    import { Timeline, Timeliner } from 'react-imation/timeline'\n\nTimeline as a component is super-handy. It manages the state of `time`.\n\n```jsx\n\u003cTimeline\n  playOnMount={true}\n  min={0}\n  max={100}\n  loop={true}\u003e\n{({time, playing, togglePlay, setTime}) =\u003e\n  \u003cdiv\u003e\n\n    The timeline is {playing ? '' : 'not '}playing! \u003cbr /\u003e\n    Current time is {time}. \u003cbr /\u003e\n\n    We can easily create a pause button like this:\u003cbr /\u003e\n    \u003cbutton onClick={togglePlay}\u003e\n      {playing ? 'pause' : 'play'}\n    \u003c/button\u003e\n\n    \u003cbr /\u003e\n\n    ... or jump around the timeline: \u003cbr /\u003e\n    \u003cbutton onClick={event =\u003e setTime(50)}\u003e\n      Jump to 50\n    \u003c/button\u003e\n\n    ... and tween to spin some text:\n    \u003ch2\n      style={tween(time, [\n        [   0, { transform: rotate(0)   } ],\n        [ 100, { transform: rotate(360) } ]\n      ])}\n    \u003e\n      spin\n    \u003c/h2\u003e\n\n  \u003c/div\u003e\n}\u003c/Timeline\u003e\n```\n\n#### `\u003cTimeline /\u003e`: overview\n\nIt accepts a single child which should be a function.\nWhen rendered, Timeline calls the function by passing in as the first\nargument an instance of the `Timeliner` class.\n\n**Note:** Because this is a stateful component, it will work well for simple\nuse-cases. If you have more complex needs, using the `timeliner` prop\n(described below) might get you a *bit* further, but consider using\nthe following lighter-weight stateless abstractions instead:\n\n  - [`\u003cInterval /\u003e`](#interval-)\n  - [`animationFrame`](#animationframe)\n  - [`\u003cAnimationFrame /\u003e`](#animationframe-)\n\nthey compose well in a system\nwith reactive state management. Check out\n[react-three-ejecta-boilerplate](https://github.com/gilbox/react-three-ejecta-boilerplate)\nwhich is an example that utilizes\n[react-stateful-stream](https://github.com/gilbox/react-stateful-stream)\nfor state management.\n\n\n#### `\u003cTimeline /\u003e`: the [**`Timeliner`**](https://github.com/gilbox/react-imation/blob/master/src/timeline/timeline.js) class and `timeliner` prop\n\nThe [`Timeliner`](https://github.com/gilbox/react-imation/blob/master/src/timeline/timeline.js)\nclass does the heavy lifting of scheduling animation\nframes and storing the value of `time`. When using the `\u003cTimeline /\u003e`\ncomponent you can provide or omit a `timeliner` prop. By omitting the\n`timeliner` prop you are instructing `\u003cTimeline /\u003e` to instantiate and\nmanage an instance of the `Timeliner` class all by itself.\n\nIn many cases,\nomitting the `timeliner` prop works very well. However, sometimes you\nneed the added flexibility of *lifting* the state management functionality\noutside of the `\u003cTimeline /\u003e` component. Here's what it looks like when\nwe provide a `timeliner` prop:\n\n```jsx\nconst timeliner = new Timeliner();\ntimeliner.play();\n\n\u003cdiv\u003e\n  \u003cbutton\n    onClick={() =\u003e\n      this.setState({showTimeline: !this.state.showTimeline})\n    }\u003e\n      Toggle\n  \u003c/button\u003e\n\n  {this.state.showTimeline \u0026\u0026\n    \u003cTimeline timeliner={timeliner}\u003e\n    {({time}) =\u003e\n      `The current time is {time}`\n    }\u003c/Timeline\u003e\n\u003c/div\u003e\n```\n\nNotice how we can mount/unmount the `\u003cTimeline /\u003e` component\nwithout losing it's state, and since the `timeliner` instance has\nbeen lifted outside of the `\u003cTimeline /\u003e` component, when the component\nis re-mounted it works the same as if it had been mounted all along.\n\nThe single most important property of the `Timeliner` class is `time`.\nLet's take a look at the function we passed in as the child of the\n`\u003cTimeline /\u003e` component from the previous example:\n\n    ({time}) =\u003e `The current time is {time}`\n\nRemember, when `\u003cTimeline /\u003e` calls this function it will pass in\nan instance of the `Timeliner` class. Our function uses *object destructuring*\nto get the value of the `time` property.\n\nYou can access *methods* on the `Timeliner` instance via destructuring\nas well. All of the methods exposed by `Timeliner` are automatically\nbound to the `Timeliner` instance so that they work in this way.\n\n#### `\u003cTimeline /\u003e`: the partially applied `tween` function\n\nThe Timeliner class exposes a `tween` method which is the same `tween` function\nwe've discussed, with the first argument already applied. The following two expressions\nare equivalent:\n\n    tween(timeliner.time, [[0,0], [60,100]]);\n\n    timeliner.tween([[0,0], [60,100]]);\n\nThe happy consequence is that with `\u003cTimeline /\u003e` you can use destructuring\nto easily access `Timeliner#tween`:\n\n```jsx\n\u003cTimeline\u003e\n{({tween}) =\u003e\n  \u003ch1 style={tween([\n    [  0, { color: rgb(0,0,255) } ],\n    [ 60, { color: rgb(255,0,0) } ]\n  ])}\u003e\n    I change color!\n  \u003c/h1\u003e\n}\u003c/Timeline\u003e\n```\n\n\n## react-native support\n\nSupports react-native as of `v0.2.6`, however performance is not so good\nbecause react-native works best when native props are manipulated directly.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgilbox%2Freact-imation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgilbox%2Freact-imation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgilbox%2Freact-imation/lists"}