首页 > 开发 > JS > 正文

在React中写一个Animation组件为组件进入和离开加上动画/过度效果

2024-05-06 16:52:39
字体:
来源:转载
供稿:网友

在React中写一个Animation组件为组件进入和离开加上动画/过度效果

问题

在单页面应用中,我们经常需要给路由的切换或者元素的挂载和卸载加上过渡效果,为这么一个小功能引入第三方框架,实在有点小纠结。不如自己封装。

思路

原理

以进入时 opacity: 0 --> opacity: 1  ,退出时 opacity: 0 --> opacity: 1 为例

元素挂载时

1.挂载元素dom
2.设置动画 opacity: 0 --> opacity: 1

元素卸载时

1.设置动画 opacity: 0 --> opacity: 1 
2.动画结束后卸载dom

组件设计

为了使得组件简单易用、低耦合,我们期望如下方式来调用组件:

在 App.jsx 里调用组件:

通过改变isShow的值来指定是否显示

// App.jsx
// 其他代码省略
import './app.css';
<Animation isShow={isShow} name='demo'>
    <div class='demo'>
        demo
    </div>
</Animation>
// 通过改变isShow的值来指定是否显示
在 App.css 里指定进入离开效果:
// 基础样式
.demo {
    width: 200px;
    height: 200px;
    background-color: red;
}
// 定义进出入动画
.demo-showing {
    animation: show 0.5s forwards;
}
.demo-fading {
    animation: fade 0.5s forwards;
}
// 定义动画fade与show
@keyframes show {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
@keyframes fade {
    from {
        opacity: 1;
    }
    to {
        opacity: 0;
    }
}

根据思路写代码

// Animation.jsx
import { PureComponent } from 'react';
import './index.css';
class Animation extends PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            isInnerShow: false,
            animationClass: '',
        };
    }
    componentWillReceiveProps(props) {
        const { isShow } = props;
        if (isShow) {
            // 显示
            this.show().then(() => {
                this.doShowAnimation();
            });
        } else {
            // 隐藏
            this.doFadeAnimation();
        }
    }
    handleAnimationEnd() {
        const isFading = this.state.animationClass === this.className('fading');
        if (isFading) {
            this.hide();
        }
    }
    show() {
        return new Promise(resolve => {
            this.setState(
                {
                    isInnerShow: true,
                },
                () => {
                    resolve();
                }
            );
        });
    }
    hide() {
        this.setState({
            isInnerShow: false,
        });
    }
    doShowAnimation() {
        this.setState({
            animationClass: this.className('showing'),
        });
    }
    doFadeAnimation() {
        this.setState({
            animationClass: this.className('fading'),
        });
    }
    /**
     * 获取className
     * @param {string} inner 'showing' | 'fading'
     */
    className(inner) {
        const { name } = this.props;
        if (!name) throw new Error('animation name must be assigned');
        return `${name}-${inner}`;
    }
    render() {
        let { children } = this.props;
        children = React.Children.only(children);
        const { isInnerShow, animationClass } = this.state;
        const element = {
            ...children,
            props: {
                ...children.props,
                className: `${children.props.className} ${animationClass}`,
                onAnimationEnd: this.handleAnimationEnd.bind(this),
            },
        };
        return isInnerShow && element;
    }
}
export default Animation;

Demo示例

点我直达


注:相关教程知识阅读请移步到JavaScript/Ajax教程频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表