我正在尝试使用TransitionGroup和TweenMax设置自定义模态组件的动画。要理解模态的结构,请考虑以下假设的伪结构:
<Container>
<Content>
// modal contents in here...
</Content>
<Overlay/>
</Container>
我希望<Container>
淡入,<Content>
从底部淡入。这是一个可以帮助您理解的视觉效果;红色代表叠加层,白色代表内部内容:
我实际上使用以下代码获得了上述视觉效果:
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
方法而不使用更高阶的组件吗?我非常感谢你的意见。谢谢!
答案 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-move
(https://github.com/react-tools/react-move)代替transitionGroup
,这是我们问题的解决方案。它非常方便,您可以自定义持续时间,缓和曲线,在单个过渡中定义不同风格的不同动画。
然后您不需要TweenMax
来处理动画。希望它会对你有所帮助。