react组件之间的动画过渡

IT技术 css reactjs reactcsstransitiongroup
2021-04-08 00:57:05

我想在两个组件之间设置动画,其中第一个组件淡出并在下一个组件添加到 DOM 并淡入之前从 DOM 中删除。否则,新组件将添加到 DOM 并在旧组件被删除。你可以在这个小提琴中看到问题:

http://jsfiddle.net/phepyezx/4

// css snippet
.switch-enter {
    opacity: 0.01;
}
.switch-enter.switch-enter-active {
    opacity: 1.0;
}
.switch-leave {
    opacity: 1.0;
}
.switch-leave.switch-leave-active {
    opacity: 0;
}

// React snippet 
<ReactCSSTransitionGroup transitionName="switch">
    <div key={key} className={className}>{this.text()}</div>
</ReactCSSTransitionGroup>

一个不可接受的解决方案(对我来说)是在转换到新组件之前用 css 隐藏原始组件,如下所示:

http://jsfiddle.net/phepyezx/5

// Change to css
.switch-leave {
    visibility: hidden;
    height: 0px;
    width: 0px;
    opacity: 1.0;
}

有没有办法在移除原始组件之前“延迟”安装新组件的react?我对速度或其他一些库持开放态度来实现这一点。

谢谢

3个回答

另一种解决方案是让传入和传出元素占据相同的空间,例如让它们都绝对定位:

<ReactCSSTransitionGroup
    className="container"
    component="div"
    transitionName="switch">
...

.container {
    position: relative;
}
.container > div {
    position: absolute;
}

http://jsfiddle.net/phepyezx/7/


您可以使用transition-delay等待离开组件消失,然后再使进入组件出现,例如:

.fade-enter {
  opacity: 0.01;
}
.fade-enter.fade-enter-active {
  opacity: 1;
  transition: opacity 1s;
  transition-delay: 1s;
}

.fade-leave {
  opacity: 1;
}
.fade-leave.fade-leave-active {
  opacity: 0.01;
  transition: opacity 1s;
}
是的。不过,与完全淡出原始效果并不完全相同。我想如果没有诡计,我所追求的是不可能的。我想我可以使用较低级别的 ReactTranssionGroup 的 componentDidLeave() 生命周期方法来设置状态,以便知道何时开始呈现新组件。另一种方法是使用普通的 javascript 在底层 DOM 节点上应用 transitionend 事件侦听器,如本文所示:chloechen.io/react-animation-done-in-two-ways
2021-05-29 00:57:05
这非常有效,特别是当与 flexbox 搭配使用时,可以调整正在传入和传出的组件的大小。使用带有 position:relative 的父容器和带有 position:absolute 的子容器比我之前使用的 hack 方法要好得多。我创建的 React 选项卡/面板组件现在完全按预期工作,包括面板之间的重叠过渡。
2021-05-29 00:57:05
你有什么理由反对绝对定位?如果您不指定 top/bottom/left/right,它将默认为它的静态位置(换句话说,它不会移动),它不会占用任何空间。
2021-06-01 00:57:05
非常感谢!几乎完美,但需要绝对定位。不幸的是,在传出组件离开之前,我没有发现传入组件未安装(或显示:'none')的任何内容。
2021-06-11 00:57:05
添加了一个关于 transition-delay
2021-06-19 00:57:05

使用componentWillUnmount()生命周期方法解决

http://jsfiddle.net/phepyezx/9/

这是代码:

var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;

const Off = React.createClass({
    componentWillUnmount () {
        this.props.handleTransitionEnd();
    },
    render()  {
        return (
            <div className="off button">OFF</div>
        )
    }
});

const On = React.createClass({
    componentWillUnmount () {
        this.props.handleTransitionEnd();
    },
    render()  {
        return (
            <div className="on button">ON</div>
        )
    }
});

var Switch = React.createClass({
    getInitialState: function() {
        return {
            on: false,
            transitionEnd: true
        };
    },

    toggle: function(e) {
        this.setState({
            on: !this.state.on,
            transitionEnd: false
        });
    },

    handleTransitionEnd() {
        this.setState({transitionEnd: true});
    },

    renderOff() {
        if (! this.state.on && this.state.transitionEnd) {
            return (
                <Off key="off" handleTransitionEnd={this.handleTransitionEnd} />
            )
        }
    },

    renderOn() {
        if (this.state.on && this.state.transitionEnd) {
            return (
                <On key="on" handleTransitionEnd={this.handleTransitionEnd} />
            )
        }
    },

    render: function() {
        return (
            <div>
              <button onClick={this.toggle}>Toggle</button>
              <ReactCSSTransitionGroup transitionName="switch">
                {this.renderOff()}
                {this.renderOn()}
              </ReactCSSTransitionGroup>
            </div>
        );         
    }
});

React.render(<Switch/>, document.getElementById("switch"));

以及相关的css:

.switch-enter {
    opacity: 0.01;
}
.switch-enter.switch-enter-active {
    opacity: 1.0;
    transition: opacity 500ms ease-in;
}
.switch-leave {
    opacity: 1.0;
}
.switch-leave.switch-leave-active {
    opacity: 0;
    transition: opacity 500ms ease-out;
}

您可以使用Jonny Buchanan 的答案获得相同的有效结果,答案使用绝对定位和延迟而不是componentWillUnmount()

另外,要设置动画的组件必须实现componentWillUnmount,并且将handleTransitionEnd()视为prop... 你是否发现这对其他情况来说很痛苦?
2021-06-07 00:57:05
@JonnyBuchanan 您的两种方法似乎都很有用。现在已经过去了一段时间,您发现哪种方法最适合您?@RickJolly,您的方法是否最适合某些场景?它是否可以很好地扩展到 ReactCSSTransitionGroup 中的更大列表(子项)?我的理解是,您的方法会导致一个额外的render()周期(可能每个被删除的项目都需要一个额外的周期?)。你有没有发现它有优点或缺点?(也许是调试 - 也许它更清楚额外发生了render()什么?)
2021-06-09 00:57:05

如果你想延迟下一个组件的渲染,你可以使用这样的东西:

import React, { Component } from 'react';

export default class DelayedRender extends Component {

    static propTypes = {
        delay: React.PropTypes.number.isRequired,
        children: React.PropTypes.element,
        className: React.PropTypes.string
    };

    constructor(props) {
        super(props);

        this.state = {
            render: false
        };
    }

    componentDidMount() {
        setTimeout(() => {
            const delayedClassNames = this.refs.noDelayed.className;
            this.setState({
                render: true,
                classNames: delayedClassNames
            });
        }, this.props.delay);
    }

    render() {
        const { children, className } = this.props;
        return this.state.render ?
            <div className={this.state.classNames}>{children}</div> :
            <div className={className} ref="noDelayed" ></div>;
    }
}

在你的渲染方法中:

const ROUTE_TRANSITION_TIME = 500;
const views = [];

if (shouldRenderDelayedRoute) {
    views.push(
        <DelayedRender delay={ROUTE_TRANSITION_TIME} key="book">
            <A ref="book"/>
        </DelayedRender>
    );
} else {
    views.push(<B key="library"/>);
} 

<ReactCSSTransitionGroup
   transitionEnterTimeout={ROUTE_TRANSITION_TIME}
   transitionLeaveTimeout={ROUTE_TRANSITION_TIME}
   transitionName="fade-transition"
                        >
    {views}
</ReactCSSTransitionGroup>