React Motion 中元素的动画退出(从 parent 组件接收这些元素)

Animating exit of an element in React Motion (which receives these elements from a parent component)

背景 我正在尝试为元素集合创建一个容器,每个元素都可以从集合中删除。当一个元素被移除时,我想为它的退出设置动画,我正在尝试使用 React Motion 来实现这一点。

这是上面的图表:

问题 我想到了使用 React Motion 的 TransitionMotion 组件,但在尝试编写一个需要传递给它的函数时遇到了困难。这是我的——不正确的——代码:

class Container extends Component {

  state = {
    elementStyles: {}
  }

  componentDidMount() {
    this.getElementStyles();
  }

  componentDidUpdate() {
    this.getElementStyles();
  }

  getElementStyles() {
    if (!this.props.children.length || this.hasElementStyles()) return;

    // this assumes all elements passed to the container are of equal dimensions
    let firstChild = this.refs.scroller.firstChild;

    let elementStyles = {
      width: firstChild.offsetWidth,
      height: firstChild.offsetHeight,
      opacity: 1
    };

    this.setState({
      elementStyles
    });
  }


  hasElementStyles() {
    return !isEmpty(this.state.elementStyles); // lodash to the rescue
  }

  willLeave() {
    return { width: spring(0), height: spring(0), opacity: spring(0) }
  }

  getChildrenArray() {
    return Children.toArray(this.props.children); // that's React's util function
  }

  getModifiedChild(element, props) {
    if (!element) return;

    return React.cloneElement(
      element,
      {style: props.style}
    );
  }

  getInitialTransitionStyles() {
    let elements = this.getChildrenArray();
    let result = elements.map((element, index) => ({
      key: element.key,
      style: this.state.elementStyles
    }));
    return result;
  }

  render() {
    if (this.hasElementStyles()) {

      return (
        <TransitionMotion
          willLeave={this.willLeave}
          styles={this.getInitialTransitionStyles()}
        >
            { interpolatedStyles => {
              let children = this.getChildrenArray();
              return <div ref="scroller" className="container">
                { interpolatedStyles.map((style, index) => {
                    return this.getModifiedChild(children[index], style);
                  })
                }
              </div>
            }}
        </TransitionMotion>
      );
    } else {
      return (
        <div ref="scroller" className="container">
          { this.props.children }
        </div>
      );
    }
  }

}

请注意 TransitionMotion 组件中映射函数内的这一行:return this.getModifiedChild(children[index], style)。这是错误的,因为一旦从集合中删除一个元素,this.props.children 就会发生变化,并且 children 数组的索引将不再对应于根据这些元素计算出的 styles 数组的索引children.

所以我需要的是一些聪明的方法来跟踪 props.children 在元素从集合中删除之前和之后(我想不出一个;我能想到的最好的with 在数组 return this.getModifiedChild(children.find(child => child.key === style.key), style); 上使用了 find 函数,但这会导致循环中有很多循环,我什至不敢想它),或者使用一些完全不同的方法,我是不知道。你能帮忙吗?

此解决方案的删除动画将已删除的项目(以及行中 "cards" 的其余部分)水平滑动到左侧卡片下方(使用其较低的 z-index)。

初始渲染设置了 标签并且卡片显示为一行。

每张卡片共享一个删除按钮方法,该方法设置数组元素的索引并启动切片数组的计时器。 setState 触发渲染,在计时器执行切片之前为卡片设置动画。

class CardRow extends React.PureComponent {

    constructor(props) {
        super(props);
        this.state = {
            cardToRemove: 99,
        };
    }

    findCard = (_id) => {
        return this.myArray.find((_card) => {
            return _card.id === _id;
        });
    };

    removeCardClick = (evt) => {
        const _card = this.findCard(evt.target.id);

        setTimeout(() => {  // wait for the render...
            this.myArray.splice(this.myArray.indexOf(_card), 1);
            this.setState({cardToRemove: 99});
        }, 200);

        this.setState({cardToRemove: this.myArray.indexOf(_card)});
    };

    render() {
        let items = this.myArray.map((item, index) => {

            let itemLeft = 200 * index;     // card width 200px
            if (this.state.cardToRemove <= index) {
                itemLeft = 200 * (index - 1);
            }

            // .cardContainer {position: fixed}
            return <div key={item.id}>
                <Motion style={{left: spring(itemLeft)}}>
                    {({left}) =>
                        <div className="cardContainer" style={{left: left}}>
                            <div className="card">
                                <button className="Button" id={item.id} onClick={this.removeCardClick}>Del</button>
                            </div>
                        </div>
                    }
                </Motion>
            </div>;
        });

        items = items.reverse();

        return <div>
            {items}
        </div>;
    }
}

就是这样!谢谢,享受,