QueueAnim 进出场动画

通过简单的配置对一组元素添加串行的进场动画效果。

何时使用#

  • 从内容A到内容B的转变过程时能有效的吸引用户注意力,突出视觉中心,提高整体视觉效果。

  • 小的信息元素排布或块状较多的情况下,根据一定的路径层次依次进场,区分维度层级,来凸显量级,使页面转场更加流畅和舒适,提高整体视觉效果和产品的质感。

  • 特别适合首页和需要视觉展示效果的宣传页,以及单页应用的切换页面动效。

API#

此组件取代了 0.9.x 版本的 enter-animation

元素依次进场。

<QueueAnim>
  <div key='demo1'>依次进场</div>
  <div key='demo2'>依次进场</div>
  <div key='demo3'>依次进场</div>
  <div key='demo4'>依次进场</div>
</QueueAnim>

每个子标签必须带 key,如果未设置 key 将不执行动画。

参数 类型 默认 详细
type string / array right 动画内置参数
left right top bottom scale scaleBig scaleX scaleY
animConfig object / array null 配置动画参数
{opacity:[1, 0],translateY:[0, -30]} 具体参考 velocity 的写法
delay number / array 0 整个动画的延时,以毫秒为单位
duration number / array 500 每个动画的时间,以毫秒为单位
interval number / array 100 每个动画的间隔时间,以毫秒为单位
leaveReverse boolean false 出场时是否倒放,从最后一个 dom 开始往上播放
ease string / array easeOutQuart 动画的缓动函数,查看详细
animatingClassName array ['queue-anim-entering', 'queue-anim-leaving'] 进出场动画进行中的类名
component string div QueueAnim 替换的标签名

当以上数据类型为 Array 时,['left', 'top'] 第一个为进场动画属性, 第二个为离场属性。

代码演示

import { QueueAnim } from 'antd';

ReactDOM.render(
  <QueueAnim delay={500}>
    <div key="a">依次进场</div>
    <div key="b">依次进场</div>
    <div key="c">依次进场</div>
    <div key="d">依次进场</div>
    <div key="e">依次进场</div>
    <div key="f">依次进场</div>
  </QueueAnim>
, mountNode);
.code-box-demo > div {
  overflow: hidden;
}

.code-box-demo .buttons {
  text-align: center;
  margin: 0 auto;
  margin-bottom: 20px;
}

最简单的进场例子。

import { QueueAnim, Button } from 'antd';

const Test = React.createClass({
  getInitialState() {
    return {
      show: true
    };
  },
  onClick() {
    this.setState({
      show: !this.state.show
    });
  },
  render() {
    return (
      <div>
        <p className="buttons">
          <Button type="primary" onClick={this.onClick}>切换</Button>
        </p>
        <QueueAnim className="demo-content"
          key="demo"
          type={['right', 'left']}
          ease={['easeOutQuart', 'easeInOutQuart']}>
          {this.state.show ? [
            <div className="demo-kp" key="a">
              <ul>
                <li></li>
                <li></li>
                <li></li>
              </ul>
            </div>,
            <div className="demo-listBox" key="b">
              <div className="demo-list">
              <div className="title"></div>
                <ul>
                  <li></li>
                  <li></li>
                  <li></li>
                </ul>
              </div>
            </div>
          ] : null}
        </QueueAnim>
      </div>
    );
  }
});

ReactDOM.render(<Test />, mountNode);

通过把属性设置一个数组来分别表示进出场的效果,typeanimConfigdelaydurationintervalease 等属性均支持配置为数组。

import { QueueAnim, Button, Checkbox, Radio } from 'antd';
const RadioGroup = Radio.Group;

const Test = React.createClass({
  getInitialState() {
    return {
      show: true
    };
  },
  onClick() {
    this.setState({
      show: !this.state.show
    });
  },
  render() {
    return (
      <div>
        <p className="buttons">
          <Button type="primary" onClick={this.onClick}>切换</Button>
        </p>
        <QueueAnim component="form" className="ant-form-horizontal" type="bottom" leaveReverse>
          {this.state.show ? [
            <div className="ant-form-item ant-form-item-compact" key="name">
              <label htmlFor="userName" className="col-6" required>用户名:</label>
              <div className="col-6">
                <p className="ant-form-text">大眼萌 minion</p>
              </div>
            </div>,
            <div className="ant-form-item" key="password">
              <label htmlFor="password" className="col-6" required>密码:</label>
              <div className="col-14">
                <input className="ant-input" type="password" id="password" placeholder="请输入密码" />
              </div>
            </div>,
            <div className="ant-form-item ant-form-item-compact" key="sex">
              <label className="col-6" required>您的性别:</label>
              <div className="col-14">
                <RadioGroup value="male">
                  <Radio value="male">男的</Radio>
                  <Radio value="female">女的</Radio>
                </RadioGroup>
              </div>
            </div>,
            <div className="ant-form-item" key="remark">
              <label htmlFor="remark" className="col-6" required>备注:</label>
              <div className="col-14">
                <textarea className="ant-input" id="remark" placeholder="随便写"></textarea>
                <p className="ant-form-explain">随便写点什么</p>
              </div>
            </div>,
            <div className="ant-form-item ant-form-item-compact" key="checkbox">
              <div className="col-14 col-offset-6">
                <label>
                  <Checkbox />
                  同意
                </label>
              </div>
            </div>,
            <div className="row" key="btn">
              <div className="col-16 col-offset-6">
                <Button type="primary">确定</Button>
              </div>
            </div>
          ] : null}
        </QueueAnim>
      </div>
    );
  }
});

ReactDOM.render(<Test />, mountNode);

表单组合的进场与出场动画。

import { QueueAnim, Button } from 'antd';

const Test = React.createClass({
  getInitialState() {
    return {
      show: true
    };
  },
  onClick() {
    this.setState({
      show: !this.state.show
    });
  },
  render() {
    return (
      <div>
        <p className="buttons">
          <Button type="primary" onClick={this.onClick}>切换</Button>
        </p>
        <QueueAnim type={['right', 'left']}>
        {this.state.show ? [
          <div className="demo-header" key="header">
            <div className="logo">
              <img width="30" src="https://t.alipayobjects.com/images/rmsweb/T1B9hfXcdvXXXXXXXX.svg" />
              <span>logo</span>
            </div>
            <QueueAnim component="ul">
              <li key="0"></li>
              <li key="1"></li>
              <li key="2"></li>
              <li key="3"></li>
              <li key="4"></li>
            </QueueAnim>
          </div>,
          <QueueAnim className="demo-content" key="content" delay={300}>
            <div className="demo-title" key="title">我是标题</div>
            <div className="demo-kp" key="b">
              <QueueAnim component="ul">
                <li key="0"></li>
                <li key="1"></li>
                <li key="2"></li>
              </QueueAnim>
            </div>
            <div className="demo-title" key="title2">我是标题</div>
            <div className="demo-listBox">
              <QueueAnim className="demo-list" delay={500}>
                <div className="title" key="title3"></div>
                <QueueAnim component="ul" type="bottom" key="li">
                  <li key="0"></li>
                  <li key="1"></li>
                  <li key="2"></li>
                  <li key="3"></li>
                  <li key="4"></li>
                </QueueAnim>
              </QueueAnim>
            </div>
          </QueueAnim>,
          <QueueAnim delay={1000} type="bottom" key="footerBox">
            <div className="demo-footer" key="footer"></div>
          </QueueAnim>
        ] : null}
        </QueueAnim>
      </div>
    );
  }
});

ReactDOM.render(<Test />, mountNode);

模拟一个完整的页面。

import { QueueAnim, Button } from 'antd';

const Test = React.createClass({
  getInitialState() {
    return {
      show: true
    };
  },
  onClick() {
    this.setState({
      show: !this.state.show
    });
  },
  render() {
    return (
      <div>
        <p className="buttons">
          <Button type="primary" onClick={this.onClick}>切换</Button>
        </p>
        <QueueAnim className="demo-content">
          {this.state.show ? [
            <div className="demo-kp" key="a">
              <ul>
                <li></li>
                <li></li>
                <li></li>
              </ul>
            </div>,
            <div className="demo-listBox" key="b">
              <div className="demo-list">
                <div className="title"></div>
                <ul>
                  <li></li>
                  <li></li>
                  <li></li>
                </ul>
              </div>
            </div>
          ] : null}
        </QueueAnim>
      </div>
    );
  }
});

ReactDOM.render(<Test />, mountNode);

同时支持进场和离场动画。

import { QueueAnim, Button } from 'antd';

const Test = React.createClass({
  getInitialState() {
    return {
      show: true,
    };
  },
  onClick() {
    this.setState({
      show: !this.state.show,
    });
  },
  render() {
    return (
      <div>
        <p className="buttons">
          <Button type="primary" onClick={this.onClick}>切换</Button>
        </p>
        <QueueAnim className="demo-content"
          animConfig={[
            { opacity: [1, 0], translateY: [0, 50] },
            { opacity: [1, 0], translateY: [0, -50] }
          ]}>
          {this.state.show ? [
            <div className="demo-kp" key="a">
              <ul>
                <li></li>
                <li></li>
                <li></li>
              </ul>
            </div>,
            <div className="demo-listBox" key="b">
              <div className="demo-list">
                <div className="title"></div>
                <ul>
                  <li></li>
                  <li></li>
                  <li></li>
                </ul>
              </div>
            </div>
          ] : null}
        </QueueAnim>
      </div>
    );
  }
});

ReactDOM.render(<Test />, mountNode);

通过 animConfig 来自定义动画进出场。

import { QueueAnim, Button } from 'antd';

const Test = React.createClass({
  getInitialState() {
    return {
      show: true,
      items: [
        <li key="0"></li>,
        <li key="1"></li>,
        <li key="2"></li>
      ],
    };
  },
  onClick() {
    this.setState({
      show: !this.state.show,
    });
  },
  onAdd() {
    let items = this.state.items;
    items.push(<li key={Date.now()}></li>);
    this.setState({
      show: true,
      items,
    });
  },
  onRemove() {
    let items = this.state.items;
    items.splice(items.length - 1, 1);
    this.setState({
      show: true,
      items,
    });
  },
  render() {
    return (
      <div>
        <p className="buttons">
          <Button type="primary" onClick={this.onClick}>切换</Button>
          <Button onClick={this.onAdd} style={{ marginLeft: 10 }}>添加</Button>
          <Button onClick={this.onRemove} style={{ marginLeft: 10 }}>删除</Button>
        </p>
        <div className="demo-content">
            <div className="demo-listBox" key="b">
              <div className="demo-list">
                <div className="title"></div>
                <QueueAnim component="ul" type={['right', 'left']}>
                  {this.state.show ? this.state.items : null}
                </QueueAnim>
              </div>
            </div>
        </div>
      </div>
    );
  }
});

ReactDOM.render(<Test />, mountNode);

场景里有增加或删除条目时也会触发动画。

const ReactRouter = require('react-router');
let { Router, Route, Link, hashHistory } = ReactRouter;
import { QueueAnim, Menu } from 'antd';

const App = React.createClass({
  render() {
    const key = this.props.location.pathname;
    const keys = key.replace('/', '') ? [key.replace('/', '')] : ['home'];
    return (
      <div>
        <Menu style={{ marginBottom: 10 }} mode="horizontal" selectedKeys={keys}>
          <Menu.Item key="home">
            <Link to="/">首页</Link>
          </Menu.Item>
          <Menu.Item key="page1">
            <Link to="/page1">Page 1</Link>
          </Menu.Item>
          <Menu.Item key="page2">
            <Link to="/page2">Page 2</Link>
          </Menu.Item>
        </Menu>
        <QueueAnim type={['right', 'left']} className="demo-router-wrap">
          {React.cloneElement(this.props.children || <Home />, { key })}
        </QueueAnim>
      </div>
    );
  }
});

const Home = React.createClass({
  render() {
    return (
      <div className="demo-router-child">
        <QueueAnim className="demo-content">
          <div className="demo-kp" key="a">
            <QueueAnim component="ul">
              <li key="0"></li>
              <li key="1"></li>
              <li key="2"></li>
            </QueueAnim>
          </div>
          <div className="demo-kp" key="b">
            <QueueAnim component="ul">
              <li key="0"></li>
              <li key="1"></li>
              <li key="2"></li>
            </QueueAnim>
          </div>
          <div className="demo-kp" key="c">
            <QueueAnim component="ul">
              <li key="0"></li>
              <li key="1"></li>
              <li key="2"></li>
            </QueueAnim>
          </div>
        </QueueAnim>
      </div>
    );
  }
});

const Page1 = React.createClass({
  render() {
    return (
      <div className="demo-router-child">
        <QueueAnim className="demo-content">
          <div className="demo-kp" key="b">
            <QueueAnim component="ul">
              <li key="0"></li>
              <li key="1"></li>
              <li key="2"></li>
            </QueueAnim>
          </div>
          <div className="demo-listBox">
            <QueueAnim className="demo-list" delay={200}>
              <div className="title" key="title3"></div>
              <QueueAnim component="ul" animConfig={{ opacity: [1, 0], translateY: [0, 30], scale: [1, 0.9] }} key="ul">
                <li key="0"></li>
                <li key="1"></li>
                <li key="2"></li>
              </QueueAnim>
            </QueueAnim>
          </div>
        </QueueAnim>
      </div>
    );
  }
});

const Page2 = React.createClass({
  render() {
    return (
      <div className="demo-router-child">
        <div className="demo-content">
          <div className="demo-listBox">
            <QueueAnim className="demo-list">
              <div className="title" key="title3"></div>
              <QueueAnim component="ul" animConfig={{ opacity: [1, 0], translateY: [0, 30], scale: [1, 0.9] }} key="li">
                <li key="0"></li>
                <li key="1"></li>
                <li key="2"></li>
                <li key="3"></li>
                <li key="4"></li>
                <li key="5"></li>
              </QueueAnim>
            </QueueAnim>
          </div>
        </div>
      </div>
    );
  }
});

ReactDOM.render((
  <Router history={hashHistory}>
    <Route path="/" component={App} ignoreScrollBehavior>
      <Route path="page1" component={Page1} />
      <Route path="page2" component={Page2} />
    </Route>
  </Router>
), mountNode);
#components-queue-anim-demo-router .demo-router-wrap {
  position: relative;
  width: 100%;
  margin: auto;
  height:200px;
  overflow: hidden;
}
#components-queue-anim-demo-router .queue-anim-leaving {
  position: absolute;
  width:100%;
}

router 组合的进场与出场动画。