React JS - TransitionGroup和高阶组件

时间:2017-05-29 05:17:20

标签: javascript reactjs animation greensock

我正在尝试使用TransitionGroupTweenMax设置自定义模态组件的动画。要理解模态的结构,请考虑以下假设的伪结构:

<Container>
   <Content>
     // modal contents in here...
   </Content>
   <Overlay/>
</Container>

我希望<Container>淡入,<Content>从底部淡入。这是一个可以帮助您理解的视觉效果;红色代表叠加层,白色代表内部内容: enter image description here

我实际上使用以下代码获得了上述视觉效果:

import React, { Component } from 'react';
import TransitionGroup from 'react-transition-group/TransitionGroup'; // npm install react-transition-group --save
import { TweenMax } from 'gsap'; // npm install gsap --save

class ParentChild extends React.Component {

    componentWillEnter(callback) {
        TweenMax.fromTo(this.parent, 0.33, {opacity: 0}, {opacity: 1, onComplete: callback});
        TweenMax.fromTo(this.child, 0.33, {opacity: 0, y: 100}, {opacity: 1, y: 0, onComplete: callback});
    }

    componentWillLeave(callback) {
        TweenMax.fromTo(this.parent, 0.33, {opacity: 1}, {opacity: 0, onComplete: callback});
        TweenMax.fromTo(this.child, 0.33, {opacity: 1, y: 0}, {opacity: 0, y: 100, onComplete: callback});
    }

    render() {
        const { size, children } = this.props;
        const parentStyle = {
            width: `${size}px`,
            height: `${size}px`,
            background: '#df4747',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center'
        };
        const childStyle = {
            background: '#fff',
            textAlign: 'center',
            color: '#000',
            width: '90%'
        }
        return(
            <div ref={el => this.parent = el} style={parentStyle}>
                <div ref={el => this.child = el} style={childStyle}>
                    {this.props.children}
                </div>
            </div>
        );
    }
}

class App extends Component {
    constructor() {
        super();
        this.state = {
            isVisible: false
        };
        this.handleToggle = this.handleToggle.bind(this);
        this.handleClose = this.handleClose.bind(this);
    }

    handleToggle() {
        this.setState({ isVisible: !this.state.isVisible });
    }

    handleClose() {
        this.setState({ isVisible: false });
    }

    render() {
        const { isVisible } = this.state;
        return(
            <div>
                <TransitionGroup>
                    {isVisible ?
                        <ParentChild size="150">
                            <p>I wanna fade up!</p>
                        </ParentChild>
                    : null}
                </TransitionGroup>

                <button onClick={this.handleToggle}>Toggle Visibility</button>
            </div>
        );
    }
}

export default App;

我希望借助更高阶的组件使上面的代码更易于维护,所以我尝试了以下内容。在我看来,以下代码更易于维护,因为TweenMax内容被隔离到单个组件(fadeInHOC和fadeInUpHOC)中,因此,它可以在任何类型的React组件上重用。另外,如果我想更改动画,我所要做的就是创建另一个HOC并更改包装函数。

import React, { Component } from 'react';
import TransitionGroup from 'react-transition-group/TransitionGroup'; // npm install react-transition-group --save
import { TweenMax } from 'gsap'; // npm install gsap --save

const fadeInHOC = (Component) => {
    return class extends React.Component {

        componentWillEnter(callback) {
            TweenMax.fromTo(this.container, 0.33, {opacity: 0}, {opacity: 1, onComplete: callback});
        }

        componentWillLeave(callback) {
            TweenMax.fromTo(this.container, 0.33, {opacity: 1}, {opacity: 0, onComplete: callback});
        }

        render() {
            return <Component containerRef={el => this.container = el} {...this.props}/>;
        }
    }
}

const fadeInUpHOC = (Component) => {
    return class extends React.Component {

        componentWillEnter(callback) {
            TweenMax.fromTo(this.container, 0.33, {opacity: 0, y: 100}, {opacity: 1, y: 0, onComplete: callback});
        }

        componentWillLeave(callback) {
            TweenMax.fromTo(this.container, 0.33, {opacity: 1, y: 0}, {opacity: 0, y: 100, onComplete: callback});
        }

        render() {
            return <Component containerRef={el => this.container = el} {...this.props}/>;
        }
    }
}

const Parent = fadeInHOC((props) => {
    const size = props.size;
    const style = {
        width: `${size}px`,
        height: `${size}px`,
        background: '#df4747',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center'
    }
    return(
        <div ref={props.containerRef} style={style}>
            {props.children}
        </div>
    );
});

const Child = fadeInUpHOC((props) => {
    const style = {
        background: '#fff',
        textAlign: 'center',
        color: '#000',
        width: '90%'
    }
    return(
        <div ref={props.containerRef} style={style}>
            {props.children}
        </div>
    );
});

class App extends Component {
    constructor() {
        super();
        this.state = {
            isVisible: false
        };
        this.handleToggle = this.handleToggle.bind(this);
        this.handleClose = this.handleClose.bind(this);
    }

    handleToggle() {
        this.setState({ isVisible: !this.state.isVisible });
    }

    handleClose() {
        this.setState({ isVisible: false });
    }

    render() {
        const { isVisible } = this.state;
        return(
            <div>
                <TransitionGroup>
                    {isVisible ?
                        <Parent size="150">
                            <Child>
                                <p>I wanna fade up!</p>
                            </Child>
                        </Parent>
                    : null}
                </TransitionGroup>

                <button onClick={this.handleToggle}>Toggle Visibility</button>
            </div>
        );
    }
}

export default App;

上述代码的问题是fadeInUp上的<Child>未被触发,只有<Parent>上的外部动画有效。你能告诉我如何让它按预期工作吗?我应该使用不同的方法吗?我应该坚持使用ParentChild方法而不使用更高阶的组件吗?我非常感谢你的意见。谢谢!

1 个答案:

答案 0 :(得分:0)

如果你在HOC组件中进一步包装ParentChild,转换组将无法工作,我们也通过将TransitionGroup与反应连接组件挂钩来解决同样的问题,生命周期方法将不会被触发一点都不

执行此操作的一种方法是首先使用Child包裹您的TransitionGroup组件 像:

const WrappedChild = (props) =>{
  return <TransitionGroup>
  <Child {...props}/>
  </TransitionGroup>
}

然后导出WrappedChild并将其设为Parent的子项,所有生命周期方法都可以在您的Child组件中使用。这将在你的情况下正常工作。

但是,如果您的Parent包含多个子项,则无效,例如,data.map((item)=> <Child ... />)呈现,Child本身是连接的redux组件(我们的情况)。

另一种方法是使用react-movehttps://github.com/react-tools/react-move)代替transitionGroup,这是我们问题的解决方案。它非常方便,您可以自定义持续时间,缓和曲线,在单个过渡中定义不同风格的不同动画。

然后您不需要TweenMax来处理动画。希望它会对你有所帮助。