GithubHelp home page GithubHelp logo

sghall / react-move Goto Github PK

View Code? Open in Web Editor NEW
6.6K 58.0 167.0 55.11 MB

React Move | Beautiful, data-driven animations for React

Home Page: https://react-move-docs.netlify.app

License: MIT License

JavaScript 99.67% Shell 0.33%
react animation motion movement move easing interpolation animate transition-animation

react-move's Introduction

React Table Logo

React-Move

Beautiful, data-driven animations for React. Just 3.5kb (gzipped)!

npm version npm downloads license

Features

  • Animate HTML, SVG & React-Native
  • Fine-grained control of delay, duration and easing
  • Animation lifecycle events: start, interrupt, end
  • Custom tweening functions
  • Awesome documentation and lots of examples
  • Supports TypeScript

Installation

// React ^16.3.0
npm install react-move

// React ^0.14.9 || ^15.3.0 || ^16.0.0
npm install react-move@^5.0.0

Note: The API for React Move 5.x and 6.x is exactly the same. The 5.x version just includes react-lifecycles-compat to make the library work with earlier versions of React. This adds a little to the bundle so use 6.x if you're using React 16.3+.

Upgrading from React Move 2.x and 3.x

The API for React Move has been essentially stable since the 2.0 version. The 4.0 version of React Move introduced a change that broke the hard dependency on d3-interpolate and introduced the interpolation prop. The current version of React Move will by default only do numeric interpolation and apply easing functions. If you only need to do numeric interpolation you don't need to do anything. Just upgrade and done.

To get the same interpolation found in React Move 2.x and 3.x which includes support for colors, paths and SVG transforms do this:

Install d3-interpolate:

npm install d3-interpolate

Then in your app:

import { NodeGroup } from 'react-move'
import { interpolate, interpolateTransformSvg } from 'd3-interpolate'

...
<NodeGroup
  data={this.state.data}
  keyAccessor={(d) => d.name}

  start={(data, index) => ({
    ...
  })}

  enter={(data, index) => ([ // An array
    ...
  ])}

  update={(data) => ({
    ...
  })}

  leave={() => ({
    ...
  })}

  interpolation ={(begValue, endValue, attr) => { // pass as prop
    if (attr === 'transform') {
      return interpolateTransformSvg(begValue, endValue)
    }

    return interpolate(begValue, endValue)
  }}
>
  ...children
</NodeGroup>

Demos

Documentation

The docs below are for version 6.x.x of React-Move.

Older versions:

The API for NodeGroup and Animate have not changed except for the interpolationxw prop, but if you want to refer back:

Getting Started

React Move exports just two components:

  • NodeGroup - If you have an array of items that enter, update and leave
  • Animate - If you have a singe item that enters, updates and leaves

< NodeGroup />

Component Props

Name Type Default Description
data * array An array. The data prop is treated as immutable so the nodes will only update if prev.data !== next.data.
keyAccessor * function Function that returns a string key given the data and its index. Used to track which nodes are entering, updating and leaving.
interpolation function numeric A function that returns an interpolator given the begin value, end value, attr and namespace. Defaults to numeric interpolation. See docs for more.
start * function A function that returns the starting state. The function is passed the data and index and must return an object.
enter function () => {} A function that returns an object or array of objects describing how the state should transform on enter. The function is passed the data and index.
update function () => {} A function that returns an object or array of objects describing how the state should transform on update. The function is passed the data and index.
leave function () => {} A function that returns an object or array of objects describing how the state should transform on leave. The function is passed the data and index.
children * function A function that receives an array of nodes.

< Animate />

Component Props

Name Type Default Description
show bool true Boolean value that determines if the child should be rendered or not.
interpolation function numeric A function that returns an interpolator given the begin value, end value, atrr and namespace. See docs for more.
start union:
 func
 object
An object or function that returns an obejct to be used as the starting state.
enter union:
 func
 array
 object
An object, array of objects, or function that returns an object or array of objects describing how the state should transform on enter.
update union:
 func
 array
 object
An object, array of objects, or function that returns an object or array of objects describing how the state should transform on update. Note: although not required, in most cases it make sense to specify an update prop to handle interrupted enter and leave transitions.
leave union:
 func
 array
 object
An object, array of objects, or function that returns an object or array of objects describing how the state should transform on leave.
children * function A function that receives the state.

Starting state

Before looking at the components it might be good to look at starting state. You are going to be asked to define starting states for each item in your NodeGroup and Animate components. This is a key concept and probably the most error prone for developers working with React Move. The starting state for each item is always an object with string or number leaves. The leaf keys are referred to as "attrs" as in "attribute." There are also "namespaces" which are a purely organizational concept.

Two rules to live by for starting states:

  • Don't use the strings "timing" or "events" as an attr or namespace.
  • There should never be an array anywhere in your object.

Example starting state:

// GOOD
{
  attr1: 100,
  attr2: 200,
  attr3: '#dadada'
}

// BAD
{
  attr1: [100], // NO ARRAYS
  attr2: 200,
  attr3: '#dadada'
}

A more concrete example might be:

{
  opacity: 0.1,
  x: 200,
  y: 100,
  color: '#dadada'
}

You can add "namespaces" to help organize your state:

{
  attr1: 100,
  attr2: 200,
  attr3: '#ddaabb',
  namespace1: {
    attr1: 100,
    attr2: 200
  }
}

Or something like:

{
  namespace1: {
    attr1: 100,
    attr2: 200
  },
  namespace2: {
    attr1: 100,
    attr2: 200
  }
}

You might use namespaces like so:

{
  inner: {
    x: 100,
    y: 150,
    color: '#545454'
  },
  outer: {
    x: 300,
    y: 350,
    color: '#3e3e3e'
  }
}

Starting state in NodeGroup

In NodeGroup you are working with an array of items and you pass a start prop (a function) that receives the data item and its index. The start prop will be called when that data item (identified by its key) enters. Note it could leave and come back and that prop will be called again. Immediately after the starting state is set your enter transition (optional) is called allowing you to transform that state.

<NodeGroup
  data={data} // an array (required)
  keyAccessor={item => item.name} // function to get the key of each object (required)
  start={(item, index) => ({ // returns the starting state of node (required)
    ...
  })}
>
  {(nodes) => (
    ...
      {nodes.map(({ key, data, state }) => {
        ...
      })}
    ...
  )}
</NodeGroup>

Starting state in Animate

In Animate you are animating a single item and pass a start prop that is an object or a function. The start prop will be called when that the item enters. Note it could leave and come back by toggling the show prop. Immediately after the starting state is set your enter transition (optional) is called allowing you to transform that state.

<Animate
  start={{ // object or function
    ...
  }}
>
  {state => (
    ...
  )}
</Animate>

Transitioning state

You return a config object or an array of config objects in your enter, update and leave props functions for both NodeGroup and Animate. Instead of simply returning the next state these objects describe how to transform the state. Each config object can specify its own duration, delay, easing and events independently.

There are two special keys you can use: timing and events. Both are optional. Timing and events are covered in more detail below.

If you aren't transitioning anything then it wouldn't make sense to be using NodeGroup. That said, it's convenient to be able to set a key to value when a node enters, updates or leaves without transitioning. To support this you can return four different types of values to specify how you want to transform the state.

  • string or number: Set the key to the value immediately with no transition. Ignores all timing values.

  • array [value]: Transition from the key's current value to the specified value. Value is a string or number.

  • array [value, value]: Transition from the first value to the second value. Each value is a string or number.

  • function: Function will be used as a custom tween function.

Example config object:

{
  attr1: [200],
  attr2: 300,
  attr3: ['#dadada']
  timing: { duration: 300, delay: 100 }
}

Using namespaces:

{
  attr1: [100],
  attr3: '#ddaabb',
  namespace1: {
    attr1: [300],
    attr2: 200
  },
  timing: { duration: 300, delay: 100 }
}

To have different timing for some keys use an array of config objects:

[
  {
    attr1: [200, 500],
    timing: { duration: 300, delay: 100 }
  },
  {
    attr2: 300, // this item, not wrapped in an array, will be set immediately, so which object it's in doesn't matter
    attr3: ['#dadada']
    timing: { duration: 600 }
  },
]

Example Transitions in NodeGroup

<NodeGroup
  data={this.state.data}
  keyAccessor={(d) => d.name}

  start={(data, index) => ({
    opacity: 1e-6,
    x: 1e-6,
    fill: 'green',
    width: scale.bandwidth(),
  })}

  enter={(data, index) => ({
    opacity: [0.5], // transition opacity on enter
    x: [scale(data.name)], // transition x on on enter
    timing: { duration: 1500 }, // timing for transitions
  })}

  update={(data) => ({
    ...
  })}

  leave={() => ({
    ...
  })}
>
  {(nodes) => (
    ...
  )}
</NodeGroup>

Using an array of config objects:

import { easeQuadInOut } from 'd3-ease';

...

<NodeGroup
  data={this.state.data}
  keyAccessor={(d) => d.name}

  start={(data, index) => ({
    opacity: 1e-6,
    x: 1e-6,
    fill: 'green',
    width: scale.bandwidth(),
  })}

  enter={(data, index) => ([ // An array
    {
      opacity: [0.5], // transition opacity on enter
      timing: { duration: 1000 }, // timing for transition
    },
    {
      x: [scale(data.name)], // transition x on on enter
      timing: { delay: 750, duration: 1500, ease: easeQuadInOut }, // timing for transition
    },
  ])}

  update={(data) => ({
    ...
  })}

  leave={() => ({
    ...
  })}
>
  {(nodes) => (
    ...
  )}
</NodeGroup>

Timing

If there's no timing key in your object you'll get the timing defaults. You can specify just the things you want to override on your timing key.

Here's the timing defaults...

const defaultTiming = {
  delay: 0,
  duration: 250,
  ease: easeLinear
};

For the ease key, just provide the function. You can use any easing function, like those from d3-ease...

List of ease functions exported from d3-ease

Events

You can add events on your config objects. You can pass a function that will run when the transition starts, is interrupted (an update to the data occurs) or ends.

Using Events:

{
  attr1: [100],
  attr3: '#ddaabb',
  namespace1: {
    attr1: [300],
    attr2: 200
  },
  timing: { duration: 300, delay: 100 },
  events: {
    start: () => {
      ..do stuff - use an arrow function to keep the context of the outer component
    },
    interrupt: () => {
      ..do stuff - use an arrow function to keep the context of the outer component
    },
    end: () => {
      ..do stuff - use an arrow function to keep the context of the outer component
    },
  }
}

Interpolation

You can wire your components in react-move to handle different types of interpolation using the interpolation prop in both NodeGroup and Animate. The code for interpolating strings or SVG paths can be bulky and, in many cases, it's not needed so by default components only handle numeric interpolation.

Your interpolation prop is a function that should avoid a lot of logic and computation. It will get called at high frequency when transitions fire in your components. You get the begin and end values and what the attribute name (string) is. You will also get the namespace string (less common) if you are using them in your state. See the sections on starting states and transitions for more on attrs and namespaces.

Cadillac Interpolation - Depends on d3-interpolate

To wire up a full service interpolation that will interpolate colors, paths, numbers and SVG transforms you can use a setup like this:

npm install react-move d3-interpolate

Then in your app:

import { NodeGroup, Animate } from 'react-move'
import { interpolate, interpolateTransformSvg } from 'd3-interpolate'

...
<NodeGroup
  data={this.state.data}
  keyAccessor={(d) => d.name}

  start={(data, index) => ({
    ...
  })}

  enter={(data, index) => ([ // An array
    ...
  ])}

  update={(data) => ({
    ...
  })}

  leave={() => ({
    ...
  })}

  interpolation ={(begValue, endValue, attr, namespace) => { // pass as prop
    if (attr === 'transform') {
      return interpolateTransformSvg(begValue, endValue)
    }

    return interpolate(begValue, endValue)
  }}
>
  ...children
</NodeGroup>

This setup mimics how d3.js works for selecting interpolators and will not force you to think too much about the values your are using. For example, if you use colors (in any format) they will be recognized and interpolated correctly. The interpolate function exported from d3-interpolate does a great job of guessing what you're trying to do and handles it for you but it also includes a lot of code (e.g. d3-color) that may not be needed for your project.

Numeric Interpolation Only - Default - No dependencies

To do numeric interpolation you don't need to do anything in your components. The default numeric interpolator looks like this:

// The default interpolator used in NodeGroup and Animate

const numeric = (beg, end) => {
  const a = +beg;
  const b = +end - a;

  return function(t) {
    return a + b * t;
  };
};

React-Move vs React-Motion

  • React-move allows you to define your animations using durations, delays and ease functions. In react-motion you use spring configurations to define your animations.

  • React-move is designed to easily plugin interpolation for strings, numbers, colors, SVG paths and SVG transforms. With react-motion you can only interpolate numbers so you have to do a bit more work use colors, paths, etc.

  • In react-move you can define different animations for entering, updating and leaving with the ability to specify delay, duration and ease on each individual key. React-motion allows you to define a spring configuration for each key in the "style" object.

  • React-move has lifecycle events on its transitions. You can pass a function to be called on transition start, interrupt or end. React-motion has an "onRest" prop that fires a callback when the animation stops (just the Motion component not TransitionMotion or StaggeredMotion).

  • React-move also allows you to pass your own custom tween functions. It's all springs in react-motion.

Contributing

We love contributions from the community! Read the contributing info here.

Run the repo locally

  • Fork this repo
  • npm install
  • cd docs
  • npm install
  • npm start

Scripts

Run these from the root of the repo

  • npm run lint Lints all files in src and docs
  • npm run test Runs the test suite locally
  • npm run test:coverage Get a coverage report in the console
  • npm run test:coverage:html Get an HTML coverage report in coverage folder

Go to live examples, code and docs!

react-move's People

Contributors

dependabot[bot] avatar ekatzenstein avatar kmart08 avatar mrarnoldpalmer avatar perborgen avatar peterrcook avatar richthornton avatar sghall avatar sufuf3 avatar tannerlinsley avatar techniq avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-move's Issues

Carousel example inside documentation

Problem or feature description

I'm learning on how to use React-Move, but I'm facing some difficults, like creating a photo gallery carousel.

Is it possible to create an example, like in React Motion Gallery.

I think that this is a very useful example to demonstrate, sliding things from right to left, and vice-versa.

React-Native animations

Use this library a lot for web projects (thank you for making!) — curious about the claims for react-native use — I'm assuming that animations are not run on the UI Thread (and instead run on the JS Thread).

If that is the case, the library isn't applicable to most mobile animations needs. Just a suggestion, since it was a little confusing when I used it on a react-native project.

<Animate /> state becomes out of sync with the show prop when it's toggled too quickly

Problem

If you quickly toggle the show prop a few times, the <Animate /> component becomes unable to render the state that it should. I'm guessing it happens if the previous transition hasn't been allowed to finish before toggling should start a new transition?

Steps to reproduce

Click the button in this example to toggle state. At some point the black box should stop rendering when the button is active (blue):
https://codesandbox.io/s/6jqxjomm0r

  • React-Move: 2.6.0
  • React: 16.0.0
  • Browser: Chrome

Using NodeGroup with function components with Hooks does not update

Problem or feature description

I am using NodeGroup with function component with hooks. The update is not getting triggered.

CodeSandbox

Steps to reproduce (only needed for problems)

The Linechart is animated with enter animation. However, on hovering over the circles, the update animation is not triggered. I expect the opacity to be 0.1 due to the update animation.

Versions (only needed for problems)

  • React-Move: 5.2.1
  • React: 16.8.3
  • Browser: Chrome

package-lock.json seems to be incorrect

Problem or feature description

package-lock.json seems to be incorrect
See the error screenshot

Steps to reproduce (only needed for problems)

nvm use 8
npm i

Versions (only needed for problems)

latest source code

screenshot 2019-01-11 at 10 20 52 am

Tree demo doesn't work with current reactjs

I've create a new project and trying to follow the collapsable tree demo, it throws the error as follow:

Uncaught TypeError: data2.links is not a function

The @vx package actually has been updated to @visx, so I'm wondering if the Tree from @visx doesn't work as before, specifically the current demo is using render props.

'background' css style property expecting a number not a string

Problem or feature description

I think there seems to be an issues with when transitioning colors, mainly the background css style property. It seems to be expecting a number for this property rather than a string?

Could this be an issue with PropTypes?

Steps to reproduce (only needed for problems)

Versions (only needed for problems)

  • React-Move: 5.2.1
  • React: 16.8.4
  • Browser: Chrome 72

Dynamically change State-Transformations based on a Node's current State

Thanks for this awesome library! This is not an issue, rather I am looking for some advice, that I couldn't get from the docs.
I want to determine the State-Transformations in the Leave-Function based on the Nodes current State. In my concrete case I have a Full-Width-Slideshow, where the Slides come from the right and leave to the left side. But when the Enter-Animation is interrupted the incoming Slide should leave to the right side.
Expressed in code:

    leave={data => (state.x <= 0
        ? data.transition.leave
        : data.transition.leaveReversed
    )}

I tried setState in the start-event of the leave function, where I can access the state, but it immediately gets overwritten.
Do you have any idea how to solve this without external state?

Requesting assistance on figuring out how to get grid animation working

Problem or feature description

I am trying to get a basic grid animation working. I can't seem to figure it out, I've created a codesandbox with an example.

I am looking to create a board with 'tiles' that animate from start to end location as the data-group changes, very similar to this example here: https://react-move.js.org/#/documentation/node-group

Please view the codesandbox here:
https://codesandbox.io/s/jn9yk73ok5

react-move: 2.6
react: 16
Bootstrap: 3.3.7

Any tips or assistance on how I would improve this is very much appreciated.

pause resume transition

Is it possible to have pause resume during transition?

For example, to have play, pause, resume button to control the animation.

Doesn’t Work on React Native

Thanks for React Move. It’s exactly what I’ve been looking for. Looks like it will fix all the problems I was having with React Motion. The only problem is it doesn’t work with React Native yet. Here are the two problems that are standing in its way, along with suggested fixes.

Transform Error

You can’t start up a React Native project that imports React Move because you get a Transform Error that says “couldn't find preset es2015”.

Solution

Removing the babel section from package.json, https://github.com/tannerlinsley/react-move/blob/master/package.json#L89, lets you start up the React Native project successfully

Transition Doesn’t Move

After applying the fix above, the project starts but Transition doesn’t actually move. The progress goes from 0 to 1 in just one step and so components don’t animate

Solution

Removing the timestamp from the current time calculation, https://github.com/tannerlinsley/react-move/blob/master/src/Transition.js#L220, changes it to let currentTime = now() and this gets the transition moving. (This matches the similar code from the Animate component, https://github.com/tannerlinsley/react-move/blob/master/src/Animate.js#L134)

Support for flexDuration in v2?

Problem or feature description

In v1 we could specify flexDuration so that if lots of animations were triggered at once, it would stagger them to prevent lag. Has this been removed in v2? I see the new timing object, but I'm wondering if there is an automatic way of handling rather than staggering them myself in the timing object.

Versions (only needed for problems)

  • React-Move: 2.6.0
  • React: 16.0.0
  • Browser: Chrome

Animation is not smooth on Safari

Problem or feature description

I got a list of columns to render, and hooked up react-move to do animation when a column is deleted. Only animating the width, marginLeft and marginRight of the column, then when one column is deleted, the other will slide from right to left.

It is very smooth on chrome and firefox, but not on safari, you can see the sluggish movements.

Steps to reproduce (only needed for problems)

const Columns = ({ columnIds }: propsType) => {
...
  const _onStart = (item, index) => ({
    width: 200,
    marginLeft: 16,
    marginRight: 16
  });

  const _onLeave = (item, index) => [
    {
      width: [0],
      timing: { duration: 300 }
    },
    {
      marginLeft: [0],
      marginRight: [0],
      timing: { duration: 200 }
    }
  ];
    <NodeGroup
      data={columnIds}
      keyAccessor={id => id}
      start={_onStart}
      leave={_onLeave}
    >
      {nodes => (
        <div style={{flex:1, display: "flex"}}>
          {nodes.map(({ key, data, state }) => (
            <div key={key} style={{ display: "flex", ...state }}>
              <Column key={key} id={key} />
            </div>
          ))}
        </div>
      )}
    </NodeGroup>
...
}

And the Column is just like a box with fixed width, height.

const Column = () => {
   return <div style={{width:200, height:500, backgroundColor: "red"}} />
}

Versions (only needed for problems)

  • React-Move: ^2.0.0
  • React: ^15.6.1
  • Browser: Version 10.0 (12602.1.50.0.10) (Safari only)

How to render animations with multiple stages

Maybe this is a dumb question, I'm wondering how to transit color like react-spring:

const multiAnimation = useSpring({
    from: { opacity: 0, color: 'red' },
    to: [
        { opacity: 1, color: '#ffaaee' },
        { opacity: 1, color: 'red' },
        { opacity: .5, color: '#008000' },
        { opacity: .8, color: 'black' }
    ]
  });

I can't find a way to do this in react-move

Zoomable Sunburst example

I am trying to reproduce the Zoomable Sunburst d3 example using vx and react-move. So far I've been able to reproduce the example without transitions (static example) but I'm having difficulty understanding how to incorporate react-move to perform the transitions/interpolations.

Basically the zoom works by changing/transitioning the xScale's domain and yScale's domain and range based on a selection and generating new paths along the way.

Ultimately it's this logic I'm trying try "port" to react-move.

function click(d) {
  svg.transition()
    .duration(750)
    .tween("scale", function() {
      var xd = d3.interpolate(x.domain(), [d.x0, d.x1]);
      var yd = d3.interpolate(y.domain(), [d.y0, 1]);
      var yr = d3.interpolate(y.range(), [d.y0 ? 20 : 0, radius]);
      
      return function(t) {
          x.domain(xd(t));
          y.domain(yd(t))
            .range(yr(t));
      };
    })
    .selectAll("path")
      .attrTween("d", function(d) { return function() { return arc(d); }; });
}

Any assistance would be much appreciated.

Independent Transitions

There’s a brilliant feature that Animate has but Transition doesn’t. You can have independent animations by setting immutable to false.

You can see what I mean with this codepen example. It has two Animate components both with a duration of 4 seconds. The first Animate component appears immediately but the second one only appears after 2 seconds. When the second one appears, the first one doesn’t change its stride. It keeps going at the same pace and reaches its target after 4 seconds. The second one gets to the same point after a further 2 seconds.

Have a look at this same example but using Transition instead of Animate. As soon as the second component appears, the first one slows down and they both reach the target at the same time. They aren’t behaving independently like they do in the Animate example. This example behaves like the Animate one does if you set immutable to true.

What do you think about changing Transition so that the animations can run independently like they can with Animate by setting immutable to false?

To really have independent animations, the durations also have to be independent. This already works with Animate which you can see in this codepen example. It has two Animate components. The first has a duration of 4 seconds and the second appears after 2 seconds and has a duration of 8 seconds. When the second component appears, the first one keeps going at the same pace.

This doesn’t work for the equivalent Transition codepen example. As soon as the second item appears, the first slows down and they both complete at the same time.

What do you think about changing Transition so that the duration (and easing) lives with the item?

Force update animation

Problem or feature description

I've started using this library for animate svg chart elements (a scatterplot) and it's great.
I now have the case that I need to animate my chart either when data change (and this is the standard working behaviour) and either when my chart size change (this is what I cannot find a good way for that).
As in your example Simple Bars, you pass the original data array on a NodeGroup, than using d3 scales you change the width and x position for each transition event. If you update data, that this will trigger a change in the animation. The problem is that I don't see a way to update my scale ranges with the new width/height of the chart and trigger a rendering (animated or not).

Last fact: I can't use viewBox to fix this because I can't/ don't want to scale text, line sizes etc.

Versions (only needed for problems)

  • React-Move: 2.7.0
  • React: 16.4.2
  • Browser: Chrome 68 OSX

how to measure before animating with `Transition` ?

Hello thank you for this library, been using it to create some components, working well so far.

Problem Description

I'm trying to use Transition to create a drop down menu.

To position and animate the drop down menu properly, I need to measure the width and height of the element.

How can I do this with in react-move ?

class Menu extends Component {
  constructor() {
    super();
    this.getMenuList = this.getMenuList.bind(this);
    this.getInnerDimensions = this.getInnerDimensions.bind(this);
    this.state = {
      items: [],
    };
  }

  componentWillReceiveProps(nextProps) {
    if (!this.props.open && nextProps.open) {
      this.setState({ items: [{}] });
    }
  }

  getMenuList(c) {
    this.menuList = c;
  }

  getInnerDimensions() { // where should I call getInnerDimensions? because the component is not even mounted.
    this.dimensions = {
      width: this.menuList.offsetWidth,
      height: this.menuList.offsetHeight,
    };
  }

  render() {
    const { open, children } = this.props;
    const { items } = this.state;

    return (
      <Portal open={open}>
        <Transition
          data={items}
          getKey={(_, i) => i}
          update={() => ({
            opacity: 1,
            scale: 1,
          })}
          enter={() => ({
            opacity: 0,
            scale: 0,
          })}
          leave={() => ({
            opacity: 0,
            scale: 0,
          })}
          duration={200}
          easing={bezier(0, 0, 0.2, 1)}
        >
          {interpolatedStyles => {
            if (interpolatedStyles.length === 0) {
              return null;
            }

            const { state: { opacity } } = interpolatedStyles[0];
            return (
              <MenuListWrapper>
                <MenuList
                  innerRef={this.getMenuList}
                  style={{
                    opacity,
                  }}
                >
                  {children}
                </MenuList>
              </MenuListWrapper>
            );
          }}
        </Transition>
      </Portal>
    );
  }
}

Any way to get the current data index in Transition update?

Problem Description

The Transition update method is providing data and the key, but I need to know which index it is at for conditional styling.

update={(data, value, i) => ({
        square: {
            className: 'square',
            x: i === 0 ? 2 : 120,
            y: layout.title.fontSize + layout.legend.top,
            height: layout.legend.square,
            width: layout.legend.square,
            fill: colors.fills[i],
            stroke: colors.strokes[i]
        },
        label: {
            className: 'legend',
            x: i === 0 ? 6 + layout.legend.square : 6 + layout.legend.square + 120,
            y: layout.title.fontSize + layout.legend.top + layout.legend.fontSize,
            fill: colors.label,
            textAnchor: 'start'
        }
    })
}

Steps to Reproduce

  1. Supply third arg to update method

System Information

  • Browser & Browser Version: Chrome
  • Node Version: 7.10.0
  • OS Version: Mac Sierra
  • NPM Version: 4.2.0
  • Yarn Version: 0.23.4

Caching and events

Problem or feature description

  1. When there are animations running in a NodeGroup, the internal animations list never gets cleaned up, which means if there are constantly animations on the screen then the old ones are "leaked" (aren't removed until all animations stop)

  2. On the leave attribute, as more animations are added, the start event is called more and more times (that is, its fired not only by the animations that are actually starting)

  3. Similarly to #1, the end leave callback doesn't get called until all the animations stop.

  4. Calling setState from the start leave callback causes an infinite loop.

All this makes it virtually impossible to remove animations from the DOM so as to not cause a crash.

Steps to reproduce (only needed for problems)

return (
    <svg 
      viewBox="0 0 100 100" 
      xmlns="http://www.w3.org/2000/svg" 
      style={{
        width: '400px',
        height: '700px',
        display: 'block',
        margin: 'auto'
        }} >
      <g transform="translate(50, 100)">
      {animations.map((animation) => <Animate
        key={animation.time}
        show={animation.active}
        start={{
          circle: {
            fill: '#2E86C1',
          },
          g: {
            transform: 'translate(0, 60)',
            opacity: 0,
          },
        }}
        enter={{
          circle: {
            fill: ['#2E86C1'],
          },
          g: {
            transform: 'translate(0,-25)',
            opacity: [1],
          },
          timing: { duration: 200, ease: easeSinOut },
        }}
        leave={{
          circle: {
            fill: ['#A9CCE3'],
          },
          g: {
            opacity: [.1],
            transform: ['translate(0, -200)'],
          },
          events: {
            start: () => {
              console.log('removed', animation.active)
              //setAnimations(animations.splice(animations.indexOf(animation), 1))
            }
          },
          timing: { duration: 4000, ease: easeSinOut },
        }}
        update={{
          g: {
            opacity: 1,
            transform: 'translate(0,-25)',
          },
          timing: { duration: 50, ease: easeSinOut },
        }}
        interpolation={(begValue, endValue, attr) => {
          if (attr === 'transform') {
            return interpolateTransformSvg(begValue, endValue)
          }
          return interpolate(begValue, endValue)
        }}
      >
        {({circle, g}) => (
            <g {...g}>
              <circle style={{x:0, y:0}} r="25" {...circle} />
              <text 
              fill="black" 
              fontSize="15"
              style = {{
                x: "50%", 
                y: "50%", 
                dominantBaseline: "middle",
                textAnchor: "middle",
              }} >
                {animation.chord}
              </text>
            </g>)}
    </Animate>)}
  </g></svg>
  )
}

Versions (only needed for problems)

  • React-Move: ^6.1.0
  • React: ^16.13.0
  • Browser: Chrome 80.0.3987.132

Physics based animations

Problem or feature description

First, just wanted to say what a wonderful library this is and thank you for making it! I'm coming from using animated and react-motion and have read the comparison, but was curious if there are any plans for physics based animations? Would it possible to allow other libraries like wobble to be plugged in? I'd be willing to help with whatever and submit a PR, please let me know.

Allow setting the default timing at multiple levels

Feature description

Would love to be able to set the default timing object.

Example:

import { Animate } from 'react-move'

<Animate defaultTiming={{ duration: 500 }} />
<NodeGroup defaultTiming={{ duration: 500 }} />

Can't get NodeGroup to update

Problem or feature description

I'm struggling with getting a NodeGroup to properly update even though the data array changes and the component is re-rendered. I must have gotten something small really wrong but I can't for the life of me figure it out.

Steps to reproduce (only needed for problems)

I've made a sample where I've reproduced the issue: https://codesandbox.io/s/z2102pqmn4

Versions (only needed for problems)

See versions in above sample

UPDATED: I didn't realize that codesandbox, unlike jsfiddle etc, doesn't save automatically so I had to redo the whole thing. Anyway - it's there now :)

Disable Animations

Feature Request

I would like the ability to conditionally disable animations.

Scenario

Run the animation on initial render and updates but then when a user is interacting w/ the chart, say via panning/zooming turn off animations for those updates. Right now I have a flag that will turn off duration/etc when that is happening but due to the queue system under the hood it lags a bit.

  getEnterUpdate(data: DataShape, index: number) {
    return {
      opacity: [1],
      cx: [this.props.xScale(data.x)],
      cy: [this.props.yScale(data.y)],
      timing: this.props.animated
        ? { duration: 300, delay: index * 10, ease: easeExpInOut }
        : { duration: 0 }
    };
  }

Any suggestions @sghall ?

Problems with data display when using Animate

Problem or feature description

I am using Animate to show a hover state. I am passing in the data as one of the state param in the start prop. I update the data in the update prop after a delay. But the data that I am passing in via the update prop seems to translate the underlying data. For example, the text "White" appears as rgb(255, 255, 255).

Steps to reproduce (only needed for problems)

The code for the Animate is shown below. Passing in the data as one of the state param is causing the issue.

<Animate
    show={show}
    start={{
        opacity: 0,
        data
    }}
    enter={[
        {
            opacity: [1],
            timing: {
                duration: Animation.PrimaryDuration,
                ease: easeQuadInOut
            }
        }
    ]}
    update={[
        {
            opacity: [0],
            timing: {
                duration: Animation.PrimaryDuration,
                ease: easeQuadInOut
            }
        },
        {
            opacity: [1],
            timing: {
                duration: Animation.PrimaryDuration,
                delay: Animation.PrimaryDuration,
                ease: easeQuadInOut
            }
        },
        {
            data: [data],
            timing: {
                duration: 0,
                delay: Animation.PrimaryDuration
            }
        }
    ]}
    leave={[
        {
            opacity: [0],
            timing: {
                duration: Animation.PrimaryDuration,
                ease: easeQuadInOut
            }
        }
    ]}
>
    {({ opacity, data }) => (
        <div
            ref={this.comp}
            className="hover-state"
            style={{ left: x, top: y, opacity }}
        >
            {data.map(d => (
                <div key={d.name} className="hover-state__item">
                    <div>{d.name}:</div>
                    <div>{d.value}</div>
                </div>
            ))}
        </div>
    )}
</Animate>

Hover over White using enter transition:
screenshot 2018-12-06 at 2 29 46 pm

Hover over White using update transition:
screenshot 2018-12-06 at 2 29 57 pm

Versions (only needed for problems)

  • React-Move: 2.8.0
  • React: 16.5
  • Browser: Chrome

A simpler interface for react-move

i've been having a playabout with react-move and I think we can simplify things down to the example on this typescript playground.

By letting the user supply their bits we can concentrate on the config.

Let me know what you think.

Old docs?

Problem or feature description

I am using v1.5.4 and I need to reference the old docs for Animate. I can see that the old Resonance site has been updated to be React-Move, but the Animate component has changed and I need to reference the old version. Is that kept anywhere?

Any plans for migrate React deprecated lifecycle methods

Problem or feature description

I was just curious if you have any plans for migrate React 16.x deprecated lifecycle methods?

I've seen that you are using componentWillReceiveProps in Animate and NodeGroup.

Versions (only needed for problems)

  • React-Move: 2.7.0

Peer dependency issue with npm 7

Problem or feature description

Using this package with npm v7 in a repository with Rect 17 will cause a peer dependency error. A suggested fix by @wildpow has been proposed in #71.

Animate components seem to be dependent on each other

Problem or feature description

When animating orthogonal objects with <Animate />, I experienced something that looks like a dependency between the objects.

Here's a short animation of the bug - the easing function works completely differently when there's only one object at a time. Notice the short lag in the ease-out phase right when the new element slides in
ezgif-4-f3a7232decbd

Steps to reproduce (only needed for problems)

Please note: the key prop does change between iterations and the created elements are distinct.
rendering the group:

  // for every animation in the list, create the animation element
  return <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" style={{
      width: '100%',
      height: '100%',
    }}><g transform="translate(50, 50)">
    {animations.map(a =>
        <ChordAnimation key={a.time} {...a} />
      )}
  </g></svg>

the element itself:

function ChordAnimation({chord, active}) {
  return (
    <Animate
      show={active}
      start={{
        circle: {
          fill: 'white',
        },
        g: {
                                transform: 'translate(0, 120)',
          opacity: 0,
        },
      }}
      enter={{
        circle: {
          fill: ['red'],
        },
        g: {
          transform: ['translate(0,-25)'],
          opacity: [1],
        },
        timing: { duration: 300, ease: easeCubicInOut },
      }}
      leave={{
        circle: {
          fill: ['blue'],
        },
        g: {
          opacity: [0],
          transform: ['translate(0, -50)'],
        },
        timing: { duration: 1000, ease: easeCubicInOut },
      }}
                        update={{
        g: {
          opacity: [1],
          transform: ['translate(0,-25)'],
        },
        timing: { duration: 100, ease: easeCubicInOut },
                        }}
                        interpolation={(begValue, endValue, attr) => {
                                if (attr === 'transform') {
                                        return interpolateTransformSvg(begValue, endValue)
                                }

                                return interpolate(begValue, endValue)
                        }}
    >
      {(state) => <g {...state.g}>
                                <circle stroke="grey" r="5" {...state.circle} />
                                <text fill="black" font-size="3">{chord}</text>
                        </g>}
    </Animate>

Versions (only needed for problems)

  • React-Move: 6.1.0
  • React: 16.13.0
  • Browser: Chrome 80.0.3987.149

How to continue to move to next position

Hi from your example in https://react-move-docs.netlify.app/demos/simple

The first animate in that page, it's toggle the x value to either 200 or 0 when click.
How do I make it run another 200 instead of revert to 0 but base on click ?
If the inital position.left is 0 it become 200 on first click and 400 on second click and so on.

import React, { PureComponent } from 'react'
import { Animate } from 'react-move'
import { easeExpOut } from 'd3-ease'

const trackStyles = {
  borderRadius: 4,
  backgroundColor: 'rgba(255, 255, 255, 0.7)',
  position: 'relative',
  margin: '5px 3px 10px',
  width: 250,
  height: 50,
}

class Example extends PureComponent {
  state = {
    open: false,
  }

  handleClick = () => {
    this.setState({ open: !this.state.open })
  }

  render() {
    return (
      <div>
        <button
          onClick={this.handleClick}
        >
          Toggle
        </button>
        <Animate
          start={() => ({
            x: 0,
          })}

          update={() => ({
            x: [this.state.open ? 200 : 0],
            timing: { duration: 750, ease: easeExpOut },
          })}
        >
          {(state) => {
            const { x } = state

            return (
              <div style={trackStyles}>
                <div
                  style={{
                    position: 'absolute',
                    width: 50,
                    height: 50,
                    borderRadius: 4,
                    opacity: 0.7,
                    backgroundColor: '#ff69b4',
                    WebkitTransform: `translate3d(${x}px, 0, 0)`,
                    transform: `translate3d(${x}px, 0, 0)`,
                  }}
                />
              </div>
            )
          }}
        </Animate>
      </div>
    )
  }
}

React 18 Compatability

Note: This library won't work in dev [strict] mode with React 18. This is an issue on the React side. The issue is in Tailwind as well: tailwindlabs/headlessui#80

I was hitting my head against a wall and just downgraded to React 17. That's my workaround for now.

TickGroup

Why was TickGroup removed from Resonance for React-Move? Is there an alternative for setting up axes for data visualization?

how to do nest animation like groupBar or stackBar

Problem or feature description

Steps to reproduce (only needed for problems)

Versions (only needed for problems)

  • React-Move:
  • React:
  • Browser:
<NodeGroup
        data={data}
        keyAccessor={x}
        start={(d, i) => {
          const bars = groupKeys.map((key, j) => {
              const value = d[key]
              return {
                index: j,
                key,
                x: x1Scale(key),
                y: maxHeight,
                value,
                width: barWidth,
                height: 0
              }
            })

          return {
            index: i,
            bars: bars
          }
        }}
        enter={(d, i) => {
          const bars = groupKeys.map((key, j) => {
              const value = d[key]
              return {
                index: j,
                key: key,
                x: x1Scale(key),
                y: [0],
                value: key,
                width: barWidth,
                height: [maxHeight]
              }
            })

          return {
            index: i,
            bars: bars,
            timing: { duration: 4000, delay: 200 + 10 * i }
          }
        }}
      >
{
          interpolateData => {
            console.log('===interpolateData===', interpolateData)
            return (
              <Group className={cx('r3-bar-group', className)} >
                {interpolateData.map(datum => {
                  const { key, data: rawDatum, state: d } = datum
                  console.log('=====d=====', d)
                  return (
                    <Group key={`bar-group-${d.index}-${d.x0}`} left={d.x0}>
                      {d.bars.map(bar => {
                        let {key, index, fill, data: barData} = bar
                        let commonProps = {key, index, fill, d: barData, data}
                        return (
                          <rect
                            key={`bar-group-bar-${d.index}-${bar.index}-${bar.value}-${bar.key}`}
                            x={bar.x}
                            y={bar.y}
                            width={bar.width}
                            height={bar.height}
                            fill={bar.fill || 'steelblue'}
                            stroke={stroke}
                            strokeWidth={strokeWidth}
                            onMouseMove={this.handleMouseMove(commonProps)}
                            onMouseLeave={this.handleMouseLeave}
                            onClick={this.handleClick(commonProps)}
                          />
                        )
                      })}
                    </Group>
                  )
                })}
              </Group>
            )
          }
        }
      </NodeGroup>

I can't get this work,what's wrong here?

Tutorials?

As mentioned there are so many choices when trying to decide on an animation library to learn. You have a big advantage due to your merged forces and cross-platform appliance. I just wish there were more tutorials for how to use this. That's one of the key reasons there are so many libraries and the community is spread so thin... the lack of documentation. One knows not where to turn for a community that they feel will endure the evolutionary speed of the ecosystem and the lack of docs, tutorials, etc. is a big smell for the instability of a technology. I hope you guys will come forward to claim the prize. I'm rooting for you, but we need more cowbell!

For real though, can a man get some tuts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.