Code Monkey home page Code Monkey logo

react-flip-move's Introduction

React Flip Move

build status npm version npm monthly downloads

This module was built to tackle the common but arduous problem of animating a list of items when the list's order changes.

CSS transitions only work for CSS properties. If your list is shuffled, the items have rearranged themselves, but without the use of CSS. The DOM nodes don't know that their on-screen location has changed; from their perspective, they've been removed and inserted elsewhere in the document.

Flip Move uses the FLIP technique to work out what such a transition would look like, and fakes it using 60+ FPS hardware-accelerated CSS transforms.

Read more about how it works

demo

Current Status

React Flip Move is looking for maintainers!

In the meantime, we'll do our best to make sure React Flip Move continues to work with new versions of React, but otherwise it isn't being actively worked on.

Because it isn't under active development, you may be interested in checking out projects like react-flip-toolkit.

Demos

Installation

Flip Move can be installed with NPM or Yarn.

yarn add react-flip-move

# Or, if not using yarn:
npm i -S react-flip-move

A UMD build is made available for those not using JS package managers:

To use a UMD build, you can use <script> tags:

<html>
  <body>
    <script src="https://unpkg.com/react-flip-move/dist/react-flip-move.js"></script>
    <script>
      // Will be available under the global 'FlipMove'.
    </script>
  </body>
</html>

Features

Flip Move was inspired by Ryan Florence's awesome Magic Move, and offers:

  • Exclusive use of hardware-accelerated CSS properties (transform: translate) instead of positioning properties (top, left). Read why this matters.

  • Full support for enter/exit animations, including some spiffy presets, that all leverage hardware-accelerated CSS properties.

  • Ability to 'humanize' transitions by staggering the delay and/or duration of subsequent elements.

  • Ability to provide onStart / onFinish callbacks.

  • Compatible with Preact (should work with other React-like libraries as well).

  • Tiny! Gzipped size is <5kb! ⚡

Quickstart

Flip Move aims to be a "plug and play" solution, without needing a lot of tinkering. In the ideal case, you can wrap the children you already have with <FlipMove>, and get animation for free:

/**
 * BEFORE:
 */
const TopArticles = ({ articles }) => (
  {articles.map(article => (
    <Article key={article.id} {...article} />
  ))}
);

/**
 * AFTER:
 */
import FlipMove from 'react-flip-move';

const TopArticles = ({ articles }) => (
  <FlipMove>
    {articles.map(article => (
      <Article key={article.id} {...article} />
    ))}
  </FlipMove>
);

There are a number of options you can provide to customize Flip Move. There are also some gotchas to be aware of.

Usage with Functional Components

Functional components do not have a ref, which is needed by Flip Move to work. To make it work you need to wrap your functional component into React.forwardRef and pass it down to the first element which accepts refs, such as DOM elements or class components:

import React, { forwardRef } from 'react';
import FlipMove from 'react-flip-move';

const FunctionalArticle = forwardRef((props, ref) => (
  <div ref={ref}>
    {props.articleName}
  </div>
));

// you do not have to modify the parent component
// this will stay as described in the quickstart
const TopArticles = ({ articles }) => (
  <FlipMove>
    {articles.map(article => (
      <FunctionalArticle key={article.id} {...article} />
    ))}
  </FlipMove>
);

API Reference

View the full API reference documentation

Enter/Leave Animations

View the enter/leave docs

Compatibility

Chrome Firefox Safari IE Edge iOS Safari/Chrome Android Chrome
Supported ✔ 10+ ✔ 4+ ✔ 6.1+ ✔ 10+ ✔ 6.1+

How It Works

Curious how this works, under the hood? Read the Medium post.


Wrapping Element

By default, Flip Move wraps the children you pass it in a <div>:

// JSX
<FlipMove>
  <div key="a">Hello</div>
  <div key="b">World</div>
</FlipMove>

// HTML
<div>
  <div>Hello</div>
  <div>World</div>
</div>

Any unrecognized props to <FlipMove> will be delegated to this wrapper element:

// JSX
<FlipMove className="flip-wrapper" style={{ color: 'red' }}>
  <div key="a">Hello</div>
  <div key="b">World</div>
</FlipMove>

// HTML
<div class="flip-wrapper" style="color: red;">
  <div key="a">Hello</div>
  <div key="b">World</div>
</div>

You can supply a different element type with the typeName prop:

// JSX
<FlipMove typeName="ul">
  <li key="a">Hello</li>
  <li key="b">World</li>
</FlipMove>

// HTML
<ul>
  <li key="a">Hello</li>
  <li key="b">World</li>
</ul>

Finally, if you're using React 16 or higher, and Flip Move 2.10 or higher, you can use the new "wrapperless" mode. This takes advantage of a React Fiber feature, which allows us to omit this wrapping element:

// JSX
<div className="your-own-element">
  <FlipMove typeName={null}>
    <div key="a">Hello</div>
    <div key="b">World</div>
  </FlipMove>
</div>

// HTML
<div class="your-own-element">
  <div key="a">Hello</div>
  <div key="b">World</div>
</div>

Wrapperless mode is nice, because it makes Flip Move more "invisible", and makes it easier to integrate with parent-child CSS properties like flexbox. However, there are some things to note:

  • This is a new feature in FlipMove, and isn't as battle-tested as the traditional method. Please test thoroughly before using in production, and report any bugs!
  • Flip Move does some positioning magic for enter/exit animations - specifically, it temporarily applies position: absolute to its children. For this to work correctly, you'll need to make sure that <FlipMove> is within a container that has a non-static position (eg. position: relative), and no padding:
// BAD - this will cause children to jump to a new position before exiting:
<div style={{ padding: 20 }}>
  <FlipMove typeName={null}>
    <div key="a">Hello world</div>
  </FlipMove>
</div>

// GOOD - a non-static position and a tight-fitting wrapper means children will
// stay in place while exiting:
<div style={{ position: 'relative' }}>
  <FlipMove typeName={null}>
    <div key="a">Hello world</div>
  </FlipMove>
</div>

Gotchas

  • Does not work with stateless functional components without a React.forwardRef, read more about here. This is because Flip Move uses refs to identify and apply styles to children, and stateless functional components cannot be given refs. Make sure the children you pass to <FlipMove> are either native DOM elements (like <div>), or class components.

  • All children need a unique key property. Even if Flip Move is only given a single child, it needs to have a unique key prop for Flip Move to track it.

  • Flip Move clones the direct children passed to it and overwrites the ref prop. As a result, you won't be able to set a ref on the top-most elements passed to FlipMove. To work around this limitation, you can wrap each child you pass to <FlipMove> in a <div>.

  • Elements whose positions have not changed between states will not be animated. This means that no onStart or onFinish callbacks will be executed for those elements.

  • Sometimes you'll want to update or change an item without triggering a Flip Move animation. For example, with optimistic updating, you may render a temporary version before replacing it with the server-validated one. In this case, use the same key for both versions, and Flip Move will treat them as the same item.

  • If you have a vertical list with numerous elements, exceeding viewport, and you are experiencing automatic scrolling issues when reordering an item (i.e. the browser scrolls to the moved item's position), you can add style={{ overflowAnchor: 'none' }} to the container element (e.g. <ul>) to prevent this issue.

Known Issues

  • Interrupted enter/leave animations can be funky. This has gotten better recently thanks to our great contributors, but extremely fast adding/removing of items can cause weird visual glitches, or cause state to become inconsistent. Experiment with your usecase!

  • Existing transition/transform properties will be overridden. I am hoping to change this in a future version, but at present, Flip Move does not take into account existing transition or transform CSS properties on its direct children.

Note on will-change

To fully benefit from hardware acceleration, each item being translated should have its own compositing layer. This can be accomplished with the CSS will-change property.

Applying will-change too willy-nilly, though, can have an adverse effect on mobile browsers, so I have opted to not use it at all.

In my personal experimentations on modern versions of Chrome, Safari, Firefox and IE, this property offers little to no gain (in Chrome's timeline I saw a savings of ~0.5ms on a 24-item shuffle).

YMMV: Feel free to experiment with the property in your CSS. Flip Move will respect the wishes of your stylesheet :)

Further reading: CSS will-change Property

Contributions

Contributors welcome! Please discuss new features with me ahead of time, and submit PRs for bug fixes with tests (Testing stack is Mocha/Chai/Sinon, tested in-browser by Karma).

There is a shared prepush hook which launches eslint, flow checks, and tests. It sets itself up automatically during npm install.

Development

This project uses React Storybook in development. The developer experience is absolutely lovely, and it makes testing new features like enter/leave presets super straightforward.

After installing dependencies, launch the Storybook dev server with npm run storybook.

This project adheres to the formatting established by airbnb's style guide. When contributing, you can make use of the autoformatter prettier to apply these rules by running the eslint script npm run lint:fix. If there are conflicts, the linter triggered by the prepush hook will inform you of those as well. To check your code by hand, run npm run lint.

Flow support

Flip Move's sources are type-checked with Flow. If your project uses it too, you may want to install typings for our public API from flow-typed repo.

npm install --global flow-typed # if not already
flow-typed install react-flip-move@<version>

If you're getting some flow errors coming from node_modules/react-flip-move/src path, you should add this to your .flowconfig file:

[ignore]
.*/node_modules/react-flip-move/.*

License

MIT

react-flip-move's People

Contributors

agido-freudenreich avatar alexanderotavka avatar andarist avatar chrisza4 avatar deibeljc avatar egorshulga avatar everdimension avatar gitter-badger avatar guuz avatar haste avatar henrikjoreteg avatar hypnosphi avatar iamnoturkitty avatar jorgesivil avatar joshwcomeau avatar jpeer264 avatar kylebakerio avatar morleyzhi avatar nickjs avatar nicolasraynaud-millevolts avatar npmcdn-to-unpkg-bot avatar philraj avatar remko avatar rhys-vdw avatar storybook-safe-bot avatar tobilen avatar vsaarinen avatar willemx avatar xcv58 avatar yp 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

react-flip-move's Issues

"Hero" animation

Is it possible to perform a "hero" animation (as Polymer calls them) using react-flip-move?

For example, is it possible to replicate this demo Ryan Florence showed using Magic Move, or either of these Polymer demos, or the transition when selecting an item on css-triggers?

if so, could you add an example or a codepen?

Thanks in advance (and especially for the awesome library, I'm already using it for other use cases)

Awesome use of React Stories

Hey,

I'm one of the creator of React Storybook, I just saw you've used Storybook with your app. That's a pretty cool use of it.

We experimented with a way to deploy Storybook to GH pages and it works.

We'll make it generalized soon.

We are also planning to add new notes tab next to the Action log. So, you may can write some notes may be kind of documentation like this:

var stories = storiesOf('FlipMove', module);

stories.add(
  'simple transition', 
  () => (
    <Controls duration={400} />
  ), 
  `
    This is the basic transition and you can simply use it like this:

    ~~~js
    <Controls duration={400} />
    ~~~
  `
);

I can send you a PR once we are ready. Would like for that?

Possibility to keep own ref to the particular items or make accessable

I added refs to the items to access it in my component, because I need the nodes for scrolling reason. I need the DOM node object of them.
Currently there is no way to access the underneath cloned items. Because of the propConverter decorator it's not possible to access the FlipMove refs and fetch a particular item component.
I tried with the react-shuffle library and there it's possible and everything works well (btw. inclusive enter/leave animation)
I don't know exactly how to solve this problem, because of the decorator I lose all possibilities to access the FlipMove object which could implement an getChild(childKey) method for use cases like this.

Any idea how to solve this?

Prop to Determine Pre-set Translations

Regarding "Existing transition/transform properties will be overridden", for the present what if react-flip-move checked for a prop on the children for example translate3d={[x, y, z]}. I believe this would be a valid solution for now as the user most likely will know the translations of their elements before hand. It also saves the need to parse existing CSS properties which may lessen the animation performance. For items that already have a translate3d prop I propose 2 custom options for the parent container stating what prop to transform from and whether to take into account transforms at all. Let me know what you think, I'd be happy to contribute to the implementation of this!

Also, great work on the library, I absolutely love it.

Allow for graceful interrupts

Right now, if an animation is interrupted, the results can be jerky. By considering the current translation at the start of the transition, we should be able to transition from that origin to the destination.

Doesn't animate parent size on add/remove

Hey Josh,

Really like the project. I'm using the FlipMove component inside of an isolated div, and then there are components before and after that div. The containing div's size changes without animation when you add and remove elements. I think the example here http://joshwcomeau.github.io/react-flip-move/examples/#/shuffle?_k=inhhwi gets around this by cheating a little. You'll see that the +/- buttons have the same parent as the list elements.

If you set the background of the div that contains the lis and the footer to something obscene like red you'll see what I'm talking about.

I can include an animation if this is confusing.

Is there a way to get the parent div size to animate smoothly?

Adding style to container

I was wondering if it was possible to style the container of the animated children. For example, I wanted to have the children be in a flexbox layout. Something like this:

<FlipMove style={{display: 'flex', justifyContent: 'center'}}>
    { this.renderTopArticles() }
</FlipMove>

Otherwise, would the only way of making the elements horizontal would be applying styles to the children themselves?

Thanks for any help.

Remove dist/ files from main branch, serve via CDN

I'm really sick of having to check in the built and minified js files. They clutter up diffs, and I often forget to build before committing.

The solution is to use something like jsDelivr to serve those files.

Will spend some time soon getting this done.

Enter/Exit animations

A few people have asked about Enter/Exit animations.

I think it's possible to keep in line with the FLIP methodology. Here's how I envision exit animations working:

  • When the component receives new props, we check to see if any items are about to be removed
  • We instead keep those elements, but apply an immediate style (something like transform: scale(0, 0);). The idea is to make the element take up 0px worth of space, so that when the layout is recalculated, its siblings treat it as though it wasn't there.
  • We then do the FLIP animation. There's some flexibility here, we can have presets for how removed items should be transitioned. They can fade out and shrink, or even just constrict in one axis (a vertical list could have removed items flattened!). I'm excited by this; it allows for a lot of possible customization.
  • Once the animation is complete, we remove those nodes from the DOM by re-rendering directly from the props provided.

The same process could work for Enter animations. We'd start it at transform: scale(0,0);, and expand it to its normal size while other elements shift out of the way.

This is a non-trivial change to the source code. We'd need to find a way to "hold on" to the items that are being removed. There may be unforeseen complications.

I'll try and dedicate some time in the next month to test this idea out. If anyone wants to give this implementation a shot, I'd be delighted :) Let me know if anyone has any questions.

CC @aight8

Something broken in sizing / position in v2

Hi! First off, thanks for your work on this project: I added v1.4 to a project at work last week and it's been dynamite. It's wonderful to be able to add such attractive animations to React transitions.

Here's an example of the widget I've been working on using v1.4 of react-flip-move, working as desired:

animation-working

When I tried to upgrade to v2, the positioning / sizing of elements seems to be broken.

animation-bug

I don't mean to make you responsible for solving my problem, exactly, but I wondered if it might ring any bells in terms of changes that happened in the move to v2.

Cheers!

Children not being removed

I've found 3 cases where children are not being removed from DOM:

  1. When disableAllAnimations is true.
  2. When using a browser that do not support CSS transitions.
  3. Firefox ( jsbin )

Edit: added Firefox case.

Extra Div

I recently noticed that after any divs are animated they are encapsulated within another div. To note: It says translate3d rather than translate as I am using my fork.
screen shot 2016-04-09 at 5 27 52 pm

Handling key changes

Fantastic library - just works.

I'm using it to animate lists of items, populated by a redux store and backed by a server-side API. When a user creates a new item I "optimistically" put it into the brower's redux store and assign it a temporary ID. After the server interaction completes I go back and renumber the item with the server generated ID. I think this is a reasonably common practice.

react-flip-move interprets the renumber as a "delete/add" operation and plays two animations very quickly.

I wonder how other developers are dealing with this situation?

One option I've considered is keeping a mapping in the component's state and remapping from server-generated-id to temporary-id in a selector until the component is unmounted.

Allowing FlipMove to work on elements further down the tree?

Right now, Flip Move only works on direct children. That means that something like this won't work:

<FlipMove>
  <div className="cell-wrapper">
    <Cell />
    <Cell />
  </div>
  <div className="cell-wrapper">
    <Cell />
  </div>
</FlipMove>

Sometimes, because of the joys of CSS, it is necessary to wrap elements like this. This is taken from a side project of mine, where I need the Cell components to be vertically centered within wrapper components.

Potential solution

Last night I spent an hour tinkering with the source, trying to see if it would be feasible to allow FlipMove to be used recursively. You'd specify the max depth, for performance reasons, so the API would look something like this:

<FlipMove recursiveMode={true} maxDepth={2}>
  <div className="cell-wrapper"><Cell /></div>
</FlipMove>

The problem was that it complicates the code to an absurd degree, to the point that it would make any additional features and bug fixes much more arduous. I don't want to introduce that level of complexity if it isn't necessary, so I'd like to hear from you guys to see if this is something you've encountered and/or something you want.

Workaround

In my specific use-case, I believe the following workaround would work:

<FlipMove>
  <div className="cell-wrapper">
    <FlipMove>
      <Cell />
      <Cell />
    </FlipMove>
  </div>
  <div className="cell-wrapper">
    <FlipMove>
      <Cell />
    </FlipMove> 
  </div>
</FlipMove>

Essentially, I'd let the outer FlipMove handle one axis (moving the containers left to right), and let the inner ones handle the other axis (moving individual cells up and down).

I haven't tested this yet, and I have no idea what the performance hit would be of nesting FlipMove components.

Wait it out?

The other thing to consider is this may become a moot point in the future. In Ben Alpert's fantastic "React: What Lies Ahead" talk, he mentioned that 60% of the <div>s at facebook are similar to my cell-wrapper components; they exist purely for CSS positioning reasons. Ben would like to see React handle Layout without the need for so many wrapping elements. If this becomes reality, my problem would disappear :).

Obvious caveat: That's a hypothetical solution that, at best, is a long way away.

Internal array not getting reset?

Hi,

Apologies if this is an ill-formed question/issue. I've been toying around with this library, which seems super cool and very smooth, and during one of my tests I've stumbled across something that is either a bad implementation on my part (for which I'm unclear how to resolve) or a bug in the library. I'll paste in my code below, but the gist is this: I created a simple grid, with a "shuffle" button which, well, shuffles the tiles. As a test, I wanted to change the opacity of each tile as it was moving, so I set a "moving" flag on the onStart() function, and then unset that flag on onFinish() (then, based on that flag, I set the opacity to 1 or 0.5. The first shuffle works very smoothly, but on subsequent shuffles, it took longer and longer for the opacity to reset. So I threw a console.log in the render() function, and noticed that with each click of the shuffle button, the number of renders kept going up, by the same number of tiles that moved each time. So, on a 3x3 grid, it initially rendered 9 times, then 16, then 23, and so on. In the code below, you'll notice that the number of tiles the code thinks it's rendering doesn't change, which is good--it's the total number of renders that keeps going up.

It kind of looks like the library is keeping track of the items that have moved (obviously, not every tile moves on each shuffle, hence the reason it's not 9, 18, 27, etc), and isn't resetting that counter.

So I don't know if this is a feature or a bug. If it's the former, then I'm not sure how to reset it.

Also (bonus feature request), is it possible to have something like an onAllFinish() method? I could certainly see cases where I'd like to be able to fire off an event after all the animations are done.

Thanks!!

Code below:

import React, { Component } from 'react';
import FlipMove from 'react-flip-move';
import _ from "lodash";

export default class GridContainer extends Component {

constructor (props) {
    super(props);


    this.config = {
        rows: 3,
        columns: 3,
        tileWidth: 100,
        tileHeight: 100,
        tileMarginRight: 2
    }

    this.config.numTiles = this.config.rows * this.config.columns;

    this.state = {
        tileData: _.times(this.config.numTiles, (index) => (
            {
                id: index,
                moving: false
            })
        )           
    }
}

handleShuffle = (evt) => {
    evt.preventDefault();

    this.setState({
        tileData: _.shuffle(this.state.tileData)
    })
}

handleMoveStart = (child, node) => {

    const tiles = this.state.tileData.slice(); //create new array to mutate
    const current = tiles.findIndex( (tile) => { return tile.id === child.ref; })

    tiles[current].moving = true;

    this.setState({
        tileData: tiles
    })  

};


handleMoveFinish = (child, node) => {
    const tiles = this.state.tileData.slice(); //create new array to mutate
    const current = tiles.findIndex( (tile) => { return tile.id === child.ref; })

    tiles[current].moving = false;

    this.setState({
        tileData: tiles
    })  
};


render() {
  return (
  <div>
        <div><button onClick={this.handleShuffle}>Shuffle</button></div>
        <Grid config={this.config} tileData={this.state.tileData} handleMoveStart={this.handleMoveStart} handleMoveFinish={this.handleMoveFinish} />
  </div>
  )
}
}


class Grid extends Component {

tiles = () => {

    return this.props.tileData.map ( (tile) => {
        return this.tile(tile);
    })

};


tile = (data) => {

    const style = {
        width: this.props.config.tileWidth,
        height: this.props.config.tileHeight,
        backgroundColor: "yellow",
        borderRadius: 5,
        float: "left",
        marginRight: this.props.config.tileMarginRight,
        marginBottom: 2,
        opacity: (data.moving) ? "0.5" : "1.0"
        //transition: "opacity 25 ease-in-out"
    }


    return (
        <div key={data.id} ref={data.id} style={style}>tile {data.id}</div>
    )       

};



render () {

    const gridWidth = (this.props.config.tileWidth * this.props.config.columns) + (this.props.config.tileMarginRight * this.props.config.columns);
    const gridHeight = this.props.config.tileHeight * this.props.config.rows;

    const gridStyle = {
        width: gridWidth,
        height: gridHeight
    }

    console.log("RENDERING NUM TILES:", this.props.tileData.length);

    return (
        <div style={gridStyle}>
            <FlipMove 
                duration={250}
                easing="ease-in-out"
                staggerDurationBy={10} 
                staggerDelayBy={0} 
                onStart={this.props.handleMoveStart}
                onFinish={this.props.handleMoveFinish}
            >
                {this.tiles()}
            </FlipMove>
        </div>
    )   
}
}

Wrong animation with nested transition

Hi, first of all thanks for your amazing work!

I came across the problem, when the child element has transition, in this case all the animations, including first level child ( list item for which i would like to enable animation). Hope it's clear :)

Nested <FlipMove> lists don't animate properly

I have a structure like this:

<FlipMove>
    <div class="section">
        <FlipMove>
            <div class="item">...</div>
            <div class="item">...</div>
            <div class="item">...</div>
        </FlipMove>
    </div>
    <div class="section">
        <FlipMove>
            <div class="item">...</div>
            <div class="item">...</div>
            <div class="item">...</div>
        <FlipMove>
    </div>
</FlipMove>

(pretend everything has a unique key property)

The items animate correctly, but when a section is moved all of its child items animate as well, making for a confusing jumble of motion.

(Incidentally, in my use case items can be moved between sections. When that happens the item snaps into the new section with no movement animation (the sections do animate to expand/contract, however). I don't really expect this component to support that kind of functionality, but I figured I'd mention it.)

sorting animation

Is it possible to turn off the sorting animation (and only use onEnter and onLeave)? I am using React DnD for a sortable list and would like to use FlipMove animations only for items that are deleted, but when I grab and drag an item to re-order the list there is a strange interaction. For example, if I grab item 1 and drag it above item 2, an infinite loop of 1 swapping with 2 and 2 swapping with 1 occurs until I release item 1.

For more background, in the "hover" callback React DnD is sending the two indices to Redux, which updates the list state with the new order that then is injected back in to my list container via props.

Pass attributes to container element.

I have a situation where the container element needs to have a couple attributes set. Is there a way to pass attributes on to the container element (similar to how you can currently pass styles)?

this.props.children is an opaque object

I just read through the code and noticed children being accessed as if it's always an array. Which is not true. If you have one child, it is often not an array.

The 'children' should be accessed through React.Children.* methods to be safe. :)

Look into spring-based motion

I really like React Motion. While I wouldn't want to include it as a dependency for this project, it may be possible to create an optional, spring-based transition.

Worth looking into!

React Native support?

In Ben Alpert's React.js Conf 2016 talk, he mentioned that animating lists is a massive priority in the coming months, especially for React Native; mobile apps often have tons and tons of lists, or are even list-based.

Despite finding it super intriguing, I have not yet had the chance to play with React Native at all.

Does anyone know if Flip Move has a place in React Native? Are there comparable tools already to accomplish the same task? Is the list-reordering thing strictly a DOM problem?

Fade out removed elements

I'm not sure if this should be the responsibility of FlipMove, but it would be a nice touch. Any advice on where I should be looking to learn how to implement this would be helpful.

Consider using a higher-order component to pre-convert Children props

When passing props to a child component, React will not do any automatic type conversion: it can either be an array of elements, or a single element object.

This means that in order to be assured that I can map/reduce/filter through children, I have to use the React.Children.toArray helper. This helper mutates the keys, which means I have to use it in all cases, even when it's already an array.

I'm not a fan of this added complexity, and it can complicate a few other things as well (eg. PropType validation).

One possible solution is to use a higher-order component to decorate the FlipMove component and do this conversion there. There is a module that appears to do just that.

Merge two wrapping component to one

Hi, how should I merge 2 component when both component must be the parent of the child List elements to perform the actions on them?

Take for example, import 2 different Animate react library, both make some animation on a list of rendered items.

import Animate1 from 'react-animate1';
import Animate2 from 'react-animate2';

The items I wish to apply the animations to:

renderItems() {
  return items.map(item=> <Item key={item.id} />);
}

Best way to apply both to the items?

<Animate1>
  <Animate2>
    {this.renderItems()}
  </Animate2>
</Animate1>

This code will break Animate1, it won't be applied to the items inside the Animate2.

Any idea how to do this, maybe modifying the Animate1 source code to apply the effects to the second-level child elements?
Is there a good approach to solve this problem without altering the wrapper components source code, perhaps creating a new component and compose the two Animate wrapper component in some way?

Example of onStart function

Great library.
This is not an issue. Can you write an example of how a onStart function would look like.

I want to animate the list items (flex items) when they first appear. Similar to transitionAppear on react-addons-css-transition-group. I am using React Flip Move to animate when adding/removing items and it works great, but I want to also animate onStart.

Stuttering in Webkit & Firefox

Sadly, while the animation is silky smooth in Chrome, Safari and Firefox are having some problems.

The problem is that it paints between the calculation step and the start of the animation. So the element goes from its original position, to its final position (for a handful of milliseconds), and then back to its original position to be animated to the final position.

It's relatively rare in Firefox, most animations are fine. It's abysmal in Safari though.

This is not an easy fix, because I don't have low-level control over when the paint happens, and I do need the element to end up in its final position to do the tallying. Maybe I could create clones of all elements, with 0 opacity, and use them to derive the newly-computed state.

Enter/Leave Animations

In the readme you have:

In the meantime, you should be able to easily add enter/exit transitions with ReactCSSTransitionGroup. I haven't personally tested it, but the two should play very nicely together, allowing you to combine custom enter/exit animations with Flip Move.

I tried doing this and ran into some issues. With code roughly like:

<ReactCSSTransitionGroup component={FlipMove} typeName="div" className="row" transitionName="example" transitionEnterTimeout={500} transitionLeaveTimeout={300}>
  {...}
</ReactCSSTransitionGroup>

I get Uncaught (in promise) TypeError: Cannot read property 'componentWillAppear' of undefined in Chrome.

The other issue is that when removing FlipMove from the mix (and just using ReactCSSTransitionGroup) I noticed that the transition group will keep the leaving elements in the DOM until the animation has completed. I believe this would cause issues for FlipMove since it won't be able to know where the remaining elements will be transitioning to until the after the leave animation. Obviously I wasn't able to actually test this though.

In any case, great library, was able to drop it in and have it work without issue and I thought you might like some feedback on the ReactCSSTransitionGroup stuff.

FlipMove does not take CSS scale() into account.

Hello,

If FlipMove is used inside an element that is scaled the animation start positions are not correctly calculated. An easy example of this is to run this code on this page and use the demo functions:
http://joshwcomeau.github.io/react-flip-move/examples/#/shuffle

$$('#main-content')[0].style.transform = 'scale(0.5)';

I've tracked the problem down to getPositionDelta(). This should fix it on the vertical axis:

return [oldBox.left - relativeBox.left, oldBox.top / SCALE_FACTOR - relativeBox.top / SCALE_FACTOR];

I admit the use case is probably not very big. Our application is scaled to maintain the same aspect ratio and element positions on every screen. But there are other use cases to use scale().

I don't see any other way to get the correct values when using CSS scaling. This is how getBoundingClientRect() works. Do you see any other way?
Would it be an option to introduce a prop for for the scale factor? It should default to 1 of course.

I also tried extending the component to overwrite the getPositionDelta(). But because a PropConverter is used extending is "broken". When you extend FlipMove you are actually extending PropConverter so you can't access any of FlipMove's internals. Any thoughts on this?

Thanks in advance!

Temporarily disable animation

Hey, is there a way I can temporarily disable all the animations from FlipMove? like a property or something like this: <FlipMove disableAnimations={true} />.

The reason I need to do this is that there are some situations that I don't want my components to be animated, and even if I pass duration="0", it still flashes a little bit, like the animation happens extremely fast, but still I can see it.

Thanks!

FlexBox weirdness with exit animations when aligned vertically

@benwalks pointed out that there's some exit animation weirdness in v2 when using flexbox to center vertically.

I believe I solved that problem by also transitioning items that are exiting, as well as modifying the presets to reset transition, and released a beta version for testing. However, that introduced a new, much more serious problem, and this issue will try to debug that.


So, the problem occurs when the component receives new props, before updating/rendering.

FlipMove iterates through this.props.children and tries to recalculate the bounding boxes for everything currently rendered. In order to actually find the DOM nodes, refs are set on render to the elements, by key. So, given the following JSX:

<FlipMove>
  <div key="hi" />
  <div key="bye" />
</FlipMove>

We have a refs object that looks like:

{
  hi: (a React Element for that node),
  bye: (another React Element for this node)
}

The crash occurs because it can't find the DOM node that corresponds to this.refs[child.key].

This can mean one of two things:

  • The DOM node no longer exists. It was removed by some external process.
  • The ref to the node wasn't populated successfully. The node exists, but React doesn't have a ref to its element.

The latter reason seems unlikely to me, because on every render, I refresh the refs for all children. When the component receives new props, we iterate through the current props, so unless the render is happening in super rapid succession, it's not this.

The former reason also seems unlikely to me, because unless you have some third party system modifying the DOM, I don't see how it would be modified between props.

Annoyingly, I'm unable to replicate the issue locally. I set up a very similar Story (https://github.com/joshwcomeau/react-flip-move/blob/feature/flexVertical/stories/github-issues.js#L25), but it works just fine there.

@benwalks, would you be able to take a look at that story and see if there are any differences? Can you also let me know if there are any other packages running that could affect the DOM? Does the problem happen when you are shuffling, removing, or adding an item?

Collapsing margins

It seems like flip-move doesn't currently take collapsed margins into account when animating items. This is on 2.4.0-rc2.

Error when setting enterAnimation and leaveAnimation props to false.

Hello,

First off, thank you for your work on this project! I absolutely love it to death.

I wanted to disable the v2.0 enter/leave animations (I like it but my boss had reservations). When I set the props, to false, I received this error:

FlipMove.js:438 Uncaught TypeError: Cannot read property 'left' of undefined

Worth noting that it still behaved properly and did disable the animation, but I did encounter this issue.

Refactor code to gather DOM manipulation in one place

As it stands, Flip Move does what it needs to do in a very linear way; methods on the component are grouped by function (computeInitialStyles, addChildToAnimationsList, runAnimation, etc).

I think it would be better if anything that touches the DOM is moved to an external module. There are a bunch of benefits to this:

  • Easier testing, can mock out those methods in unit tests
  • Support for different rendering environments (React Native?)
  • Cleaner, more declarative code

Request: add onStartAll callback

... to pair with onFinishAll :)

Example use case: having an animating class on the parent node only when the animation is happening.

Problem with leave animation and scroll

Animation start break if you have scroll, you scroll to the end and start delete items from the list;
staggerDurationBy={150}
duration={350}
enterAnimation='fade'
leaveAnimation='fade'

Improve documentation formatting

Right now, the documentation exists in a massive table. This worked alright originally, but it's feeling really cluttered now. Plus, it looks all messed up on NPM.

I'd much rather move to a simple, markdown-based system like Caolan's Async, with the name of the prop in a big-ish header, a paragraph explaining what it does, and sub-headers for accepted params (and their types), default values, and examples (where applicable).

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.