React中如何不使用插件实现组件出现或消失动画

本文作者:IMWeb 结一 原文出处:IMWeb社区 未经同意,禁止转载

首先React本身是有动画插件的React.addons.TransitionGroup,当你使用该组件时,会添加对应的动画生命周期函数来控制动画,分别为componentWillEnter,componentDidEnter,componentWillLeave,componentDidLeave。而更高级点的ReactCSSTransitionGroup则是ReactTransitionGroup是基于ReactTransitionGroup的,在React组件进入或者离开DOM的时候,它是一种简单地执行CSS过渡和动画的方式。

今天我们来探讨的是另一种实现方式,而非使用官方的插件。先抛开React,我们一般实现动画都是添加或删除对应的动画class,这是因为DOM结构本身就存在,所以可以任意操作,而React则不同,每个组件都是有生命周期的,componentDidMount则是组件挂载到DOM结构,而componentWillUnmount则在组件被移除DOM前调用。所以我们可以使用外包一层,把控制动画的责任落在这个已经存在的DOM结构上。

简单示意如下:CustomContent为React组件,这里要实现的就是它的出现或消失动画,.animate-wrap为包裹的外层

class Page extends Component {
    render() {
        return (
            <div className="page">
                <header className="header">Header</header>
                <div className="animate-wrap">
                    <CustomContent isShow={this.props.contentIsShow} />
                </div>
            </div>
        )
    }
}

下面我们继续构造,当content隐藏的时候,.animate-wrap隐藏,当content显示的时候,显示.animate-wrap并为其添加class动画

class Page extends Component {
    render() {
        let { contentIsShow, toggleContent } = this.props;
        return (
            <div className="page">
                <header className="header">Header</header>
                <div ref="animateWrap" className={ contentIsShow ? "animate-wrap active down-in" : "animate-wrap"}>
                    <CustomContent isShow={ contentIsShow } clickHandler={ this.clickHandler.bind(this) } />
                </div>
            </div>
        )
    }
}

关键css大概如下(动画设计可参看移动端重构实战系列4——进入离开动画):

.animate-wrap{
    display: none; // 默认不显示
    &.active{
        display: block; // active状态显示
    }
}
// 进入动画
.down-in{
    animation: downIn 0.3s both;
}
@keyframes downIn{
    from{
        transform: translate(0, 100%);
        opacity: 0;
    }
    to{
        transform: translate(0, 0);
        opacity: 1;
    }
}
// 离开动画
.down-out{
    animation: downOut 0.3s both;
}
@keyframes downOut{
    from{
        transform: translate(0, 0);
        opacity: 1;
    }
    to{
        transform: translate(0, 100%);
        opacity: 0;
    }
}

进入动画之后,动画结束之时应该去掉动画的class.donw-in,这就得使用DOM事件来处理了,在componentDidMount中添加监听事件,而在componentWillUnmount中移除监听事件

而最后content消失的时候则需要先添加down-outclass,再在动画结束之后移除该class,并且改变contentIsShow的值

// 判断使用哪个end事件
function whichEndEvent() {
        var k
            el = document.createElement('div');

        var animations = {
            "animation" : "animationend",
            "WebkitAnimation": "webkitAnimationEnd"
        }

        for(k in animations) {
            if(el.style[k] !== undefined) {
                return animations[k];
            }
        }
}

//在前面render的基础上添加以下动画控制
class Page extends Component {
  custructor(props){
      supor(props);
    this.animateEnd = whichEndEvent();
  }
  componentDidMount() { 
      // 监听动画结束事件,使用onAnimationEnd时,直接砍掉
      let dWrap =  ReactDOM.findDOMNode(this.refs.animateWrap);
    dWrap.addEventListener(this.animateEnd , this.removeAnimateClass)
  }
  componentWillUnmount() {
      // 移除动画结束事件,使用onAnimationEnd时,直接砍掉
      let dWrap =  ReactDOM.findDOMNode(this.refs.animateWrap);
    dWrap.removeEventListener(this.animateEnd , this.removeAnimateClass)

  }
  removeAnimateClass() {
      let dWrap =  ReactDOM.findDOMNode(this.refs.animateWrap);
    dWrap.classList.remove('down-in', 'down-out');
    // 如果为离开,则触发action toggleContent,设置contentIsShow为false,隐藏content
    if(dWrap.classLIst.contains('down-out')){ 
        this.props.toggleContent(!this.props.contentIsShow);
    }
  }
  clickHandler() {
      // 触发离开,先添加动画class down-out,并在动画结束后调用想用的action
    let dWrap =  ReactDOM.findDOMNode(this.refs.animateWrap);
    dWrap.classList.add('down-out');
  }
}

onAnimationEnd

2016-09-14更新

偶尔机会,发现React事件中已经有了onAnimationEnd,所以上面的DOM事件监听有点琐碎,可以直接砍掉,在.animate-wrap上添加 即可

class Page extends Component {
    render() {
        let { contentIsShow, toggleContent } = this.props;
        return (
            <div className="page">
                <header className="header">Header</header>
                <div ref="animateWrap" onAnimationEnd={ this.removeAnimateClass.bind(this) } className={ contentIsShow ? "animate-wrap active down-in" : "animate-wrap"}>
                    <CustomContent isShow={ contentIsShow } clickHandler={ this.clickHandler.bind(this) } />
                </div>
            </div>
        )
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏九彩拼盘的叨叨叨

用CSS来找出两张图的差异

其中,灰色的部分为相同的部分,并且灰色的部分的颜色值为rgb(127.5, 127.5, 127.5)。

9410
来自专栏前端知识分享

第124天:移动web端-Bootstrap轮播图插件使用

> 对于Bootstrap的JS插件,我们只需要将文档实例中的代码粘到我们自己的代码中 > 然后作出相应的样式调整

38340
来自专栏Java帮帮-微信公众号-技术文章全总结

jquery的checkbox,radio,select等方法总结

jquery的checkbox,radio,和select是jquery操作的一个难点和重点,很多前端新手对其了解不是很透彻。时间久了不用,我在写的时候有时也难...

17820
来自专栏地方网络工作室的专栏

图片自适应父元素大小,并左右上下居中的css方法

图片自适应父元素大小,并左右上下居中的css方法 前言 这种效果多见于矩形盒子里面调用不规则的图片,希望能够达到的效果。这个效果可以很简单的用css来实现,虽然...

28380
来自专栏前端小叙

CSS3与动画有关的属性transition、animation、transform对比

最近应公司需求,需要用css3做动画,终于把以前一直傻傻分不清楚的三个属性理解了。 索性在这里进行一个简单的对比,加深自己的记忆。 浏览器兼容性 CSS3 tr...

32460
来自专栏河湾欢儿的专栏

04-修改 维护

想要改变图标的位置? 新功能,加个图标? 画布太大,文件空白太多? 图标多余,要删除?

11020
来自专栏小轻论坛

平面设计师必备的AI快捷键

在开多个AI文档的情况下。来回切换是有点麻烦的,点来点去有点慢 CTRL+F6,可以来回切换。

35820
来自专栏林德熙的博客

win10 uwp 右击浮出窗在点击位置

如果需要让 Flyout 显示在指定的位置,那么请看本文。 本文主要让 MenuFlyout 出现在我们右击位置。

11110
来自专栏HTML5学堂

img中alt与title辨析

HTML5学堂:img中alt与title有何不同?本文主要从表现在含义、浏览器中的表现以及对于网站seo优化程度三个方面进行辨析,接下来我们一起来看看。 示例...

25730
来自专栏青青天空树

认识基本的mfc控件

  几乎可以在每个windows程序中都看到按钮、复选框、文本框以及下拉列表等等,这些都是控件。而且很多常用的控件已经内置到操作系统当中了,在Visual C+...

12720

扫码关注云+社区

领取腾讯云代金券