+
- {compArray.map(item => ({item.name} ))}
-
-
+ {compArray.map((item) => (
+
+ {item.name}
+
+ ))}
+
+
);
- }
+ };
render() {
- return (
-
- );
+ return (
+
+
+
+ );
}
}
-
-ReactDom.render(
, document.getElementById('__react-content'));
diff --git a/docs/examples/shortcut.tsx b/docs/examples/shortcut.tsx
new file mode 100644
index 0000000..e1ddc61
--- /dev/null
+++ b/docs/examples/shortcut.tsx
@@ -0,0 +1,53 @@
+import QueueAnim, { IQueueTypeOrArrayOrFunc } from 'rc-queue-anim';
+import React, { useState } from 'react';
+
+const d = [
+
+ 依次进入
+
,
+
+ 依次进入
+
,
+
+ 依次进入
+
,
+
+ 依次进入
+
,
+
+ 依次进入
+
,
+];
+export default () => {
+ const [type, setType] = useState
('left');
+ const [child, setChild] = useState(d);
+
+ return (
+
+
+
+ {child}
+
+
+ );
+};
diff --git a/docs/examples/simple.tsx b/docs/examples/simple.tsx
new file mode 100644
index 0000000..b4cf450
--- /dev/null
+++ b/docs/examples/simple.tsx
@@ -0,0 +1,15 @@
+/* eslint-disable no-console,react/no-multi-comp */
+import QueueAnim from 'rc-queue-anim';
+import React from 'react';
+
+export default () => {
+ return (
+
+ 依次进入
+ 依次进入
+ 依次进入
+ 依次进入
+ 依次进入
+
+ );
+};
diff --git a/docs/examples/switch-forcedReplay.tsx b/docs/examples/switch-forcedReplay.tsx
new file mode 100644
index 0000000..120100a
--- /dev/null
+++ b/docs/examples/switch-forcedReplay.tsx
@@ -0,0 +1,34 @@
+/* eslint-disable no-console,react/no-multi-comp */
+import QueueAnim from 'rc-queue-anim';
+import React, { useState } from 'react';
+import './assets/switch.less';
+
+const childrenKey = [{ key: 1 }, { key: 2 }, { key: 3 }, { key: 4 }, { key: 5 }, { key: 6 }];
+export default () => {
+ const [children, setChildren] = useState(childrenKey);
+ const onEnter = () => {
+ setChildren([]);
+ };
+ const onLeave = () => {
+ setChildren(childrenKey);
+ };
+ const childrenToRender = (children || []).map((item) => {
+ return ;
+ });
+ return (
+
+
鼠标经过当前区域,再移出区域查看
+
+
+ {childrenToRender}
+
+
+ {childrenToRender}
+
+
+ {childrenToRender}
+
+
+
+ );
+};
diff --git a/docs/examples/switch.tsx b/docs/examples/switch.tsx
new file mode 100644
index 0000000..82afe5a
--- /dev/null
+++ b/docs/examples/switch.tsx
@@ -0,0 +1,34 @@
+/* eslint-disable no-console,react/no-multi-comp */
+import QueueAnim from 'rc-queue-anim';
+import React, { useState } from 'react';
+import './assets/switch.less';
+
+const childrenKey = [{ key: 1 }, { key: 2 }, { key: 3 }, { key: 4 }, { key: 5 }, { key: 6 }];
+export default () => {
+ const [children, setChildren] = useState(childrenKey);
+ const onEnter = () => {
+ setChildren([]);
+ };
+ const onLeave = () => {
+ setChildren(childrenKey);
+ };
+ const childrenToRender = (children || []).map((item) => {
+ return ;
+ });
+ return (
+
+
鼠标经过当前区域,再移出区域查看
+
+
+ {childrenToRender}
+
+
+ {childrenToRender}
+
+
+ {childrenToRender}
+
+
+
+ );
+};
diff --git a/docs/examples/timeline.tsx b/docs/examples/timeline.tsx
new file mode 100644
index 0000000..77a5aa1
--- /dev/null
+++ b/docs/examples/timeline.tsx
@@ -0,0 +1,34 @@
+/* eslint-disable no-console,react/no-multi-comp */
+import QueueAnim from 'rc-queue-anim';
+import React, { useState } from 'react';
+
+const children = [
+ 依次进入
,
+ 依次进入
,
+ 依次进入
,
+ 依次进入
,
+];
+
+export default () => {
+ const [show, setShow] = useState(true);
+ return (
+
+
+
+ {show ? children : null}
+
+
+ );
+};
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..a994ac5
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,120 @@
+# rc-queue-anim
+---
+
+Animate React Component in queue, thanks to [rc-animate](https://github.com/react-component/animate) and [enter-animation](https://github.com/jljsj33/enter-animation).
+
+[![NPM version][npm-image]][npm-url]
+[![build status][github-actions-image]][github-actions-url]
+[![Codecov][codecov-image]][codecov-url]
+[![Total alerts][lgtm-alerts-image]][lgtm-alerts-url]
+[![Language grade: JavaScript][lgtm-grade-image]][lgtm-grade-url]
+[![node version][node-image]][node-url]
+[![npm download][download-image]][download-url]
+
+[npm-image]: http://img.shields.io/npm/v/rc-queue-anim.svg?style=flat-square
+[npm-url]: http://npmjs.org/package/rc-queue-anim
+[github-actions-image]: https://github.com/react-component/queue-anim/workflows/CI/badge.svg
+[github-actions-url]: https://github.com/react-component/queue-anim/actions
+[codecov-image]: https://img.shields.io/codecov/c/github/react-component/queue-anim/master.svg?style=flat-square
+[codecov-url]: https://codecov.io/gh/react-component/queue-anim/branch/master
+[lgtm-alerts-image]: https://img.shields.io/lgtm/alerts/g/react-component/queue-anim.svg?logo=lgtm&logoWidth=18&style=flat-square
+[lgtm-alerts-url]: https://lgtm.com/projects/g/react-component/queue-anim/alerts/
+[lgtm-grade-image]: https://img.shields.io/lgtm/grade/javascript/g/react-component/queue-anim.svg?logo=lgtm&logoWidth=18&style=flat-square
+[lgtm-grade-url]: https://lgtm.com/projects/g/react-component/queue-anim/context:javascript
+[node-image]: https://img.shields.io/badge/node.js-%3E=_0.10-green.svg?style=flat-square
+[node-url]: http://nodejs.org/download/
+[download-image]: https://img.shields.io/npm/dm/rc-queue-anim.svg?style=flat-square
+[download-url]: https://npmjs.org/package/rc-queue-anim
+
+## Example
+
+http://react-component.github.io/queue-anim/examples/
+
+![](https://t.alipayobjects.com/images/rmsweb/T12PliXjXgXXXXXXXX.gif)
+
+## Usage
+
+```js
+import QueueAnim from 'rc-queue-anim';
+import React from 'react';
+import ReactDom from 'react-dom';
+
+ReactDom.render(
+
+ enter in queue
+ enter in queue
+ enter in queue
+
+, mountNode);
+```
+
+## Install
+
+[![rc-queue-anim](https://nodei.co/npm/rc-queue-anim.png)](https://npmjs.org/package/rc-queue-anim)
+
+## Browser Support
+
+|![IE](https://github.com/alrra/browser-logos/blob/master/src/edge/edge_48x48.png?raw=true) | ![Chrome](https://github.com/alrra/browser-logos/blob/master/src/chrome/chrome_48x48.png?raw=true) | ![Firefox](https://github.com/alrra/browser-logos/blob/master/src/firefox/firefox_48x48.png?raw=true) | ![Opera](https://github.com/alrra/browser-logos/blob/master/src/opera/opera_48x48.png?raw=true) | ![Safari](https://github.com/alrra/browser-logos/blob/master/src/safari/safari_48x48.png?raw=true)|
+| --- | --- | --- | --- | --- |
+| IE 10+ ✔ | Chrome 31.0+ ✔ | Firefox 31.0+ ✔ | Opera 30.0+ ✔ | Safari 7.0+ ✔ |
+
+### 1.7.x add childRefs and currentRef;
+
+```js
+ {
+ this.ref = c;
+ }}
+ onEnd={() => {
+ // this..currentRef =
+ // this..childRefs.a = 1212
+ }}
+>
+ 1212
+
+```
+
+## API
+
+> You must provide the key attribute for all children of QueueAnim, children would not peform any animation without key.
+
+| props | type | default | description |
+|------------|----------------|---------|----------------|
+| type | string / array | `right` | Animation Styles
`alpha` `left` `right` `top` `bottom` `scale` `scaleBig` `scaleX` `scaleY`|
+| animConfig | object / array | null | Custom config, See below for more details [animConfig](#animConfig) |
+| delay | number / array | 0 | delay of animation |
+| duration | number / array | 450 | duration of animation |
+| interval | number / array | 100 | interval of duration |
+| leaveReverse | boolean | false | reverse animation order at leave |
+| ease | string / array | `easeOutQuart` | animation easing config like `'ease'`, `['easeIn', 'easeOut']`, `[[.42,0,.58,1]`, [.42,0,.58,1]]: [more](http://easings.net/en) |
+| appear | boolean | true | whether support appear anim |
+| component | string / React.Element | `div` | component tag |
+| componentProps | Object | null | component is React.Element, component tag props |
+| animatingClassName | array | `['queue-anim-entering', 'queue-anim-leaving']` | className to every element of animating |
+| forcedReplay | boolean | false | `leave` animation moment trigger `enter`, forced replay. |
+| onEnd | function | null | animation end callback({ key, type }), type: `enter` or `leave` |
+
+> Above props support array format, like `['left', 'top']`, the secord item is leave config. [Demo](http://react-component.github.io/queue-anim/examples/enter-leave.html)
+
+### animConfig
+
+**Data fall into three categories:**
+
+- Custom set start: `{ opacity:[1, 0] }` ;
+
default;
+
type: `{ opacity: Array }`;
+
leave automatic reverse: `{ opacity: Array }`;
+
+- Custom: `{ opacity: 0 }`;
+
Start position is not set。
+
+- Array: `[{ opacity:[1, 0] }, { opacity:[1, 0] }]`;
+
type: `[{ opacity: Array }, { opacity: Array}]`
+
+## Development
+
+```
+npm install
+npm start
+```
diff --git a/examples/animating-class.html b/examples/animating-class.html
deleted file mode 100644
index e69de29..0000000
diff --git a/examples/animating-class.js b/examples/animating-class.js
deleted file mode 100644
index d4f2a3e..0000000
--- a/examples/animating-class.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-import './assets/animating-class.less';
-
-class App extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- items: [{
- children: '依次进入1',
- key: 1,
- }, {
- children: '依次进入2',
- key: 2,
- }, {
- children: '依次进入3',
- key: 3,
- }, {
- children: '依次进入4',
- key: 4,
- }, {
- children: '依次进入5',
- key: 5,
- }, {
- children: '依次进入6',
- key: 6,
- }],
- };
- }
-
- removeAll = () => {
- this.setState({
- items: [],
- });
- }
-
- render() {
- return (
-
-
- {this.state.items.map((item) =>
- {item.children}
-
)}
-
-
-
- );
- }
-}
-
-ReactDom.render(, document.getElementById('__react-content'));
diff --git a/examples/appear.html b/examples/appear.html
deleted file mode 100644
index b3a4252..0000000
--- a/examples/appear.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
\ No newline at end of file
diff --git a/examples/appear.js b/examples/appear.js
deleted file mode 100644
index 5f746d6..0000000
--- a/examples/appear.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-ReactDom.render(
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-, document.getElementById('__react-content'));
diff --git a/examples/assets/animating-class.less b/examples/assets/animating-class.less
deleted file mode 100644
index dee2562..0000000
--- a/examples/assets/animating-class.less
+++ /dev/null
@@ -1,7 +0,0 @@
-.queue-anim-entering {
- background: #2db7f5;
-}
-
-.queue-anim-leaving {
- background: #f60;
-}
diff --git a/examples/component.html b/examples/component.html
deleted file mode 100644
index b3a4252..0000000
--- a/examples/component.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
\ No newline at end of file
diff --git a/examples/component.js b/examples/component.js
deleted file mode 100644
index 29a6d8a..0000000
--- a/examples/component.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-import Icon from 'antd/lib/icon';
-import Menu from 'antd/lib/menu';
-import 'antd/lib/style/index.less';
-import 'antd/lib/menu/style/index.less';
-
-const SubMenu = Menu.SubMenu;
-const MenuItemGroup = Menu.ItemGroup;
-function Demo() {
- return (
- Navigation One}>
-
- Option 1
- Option 2
-
-
- Option 3
- Option 4
-
-
- Navigation Two}>
- Option 5
- Option 6
-
- Option 7
- Option 8
-
-
- Navigation Three}>
- Option 9
- Option 10
- Option 11
- Option 12
-
- );
-}
-ReactDom.render(, document.getElementById('__react-content'));
diff --git a/examples/config.html b/examples/config.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/config.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/config.js b/examples/config.js
deleted file mode 100644
index 83e3730..0000000
--- a/examples/config.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-ReactDom.render(
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-, document.getElementById('__react-content'));
diff --git a/examples/custom-ease.html b/examples/custom-ease.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/custom-ease.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/custom.html b/examples/custom.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/custom.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/doubleUpdate.html b/examples/doubleUpdate.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/doubleUpdate.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/doubleUpdate.js b/examples/doubleUpdate.js
deleted file mode 100755
index f8dbcce..0000000
--- a/examples/doubleUpdate.js
+++ /dev/null
@@ -1,103 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-class App extends React.Component {
- constructor(props) {
- super(props);
- this.index = 100;
- this.items = [
- {
- children: '依次进入1',
- key: 1,
- }, {
- children: '依次进入2',
- key: 2,
- }, {
- children: '依次进入3',
- key: 3,
- }, {
- children: '依次进入4',
- key: 4,
- }, {
- children: '依次进入5',
- key: 5,
- }, {
- children: '依次进入6',
- key: 6,
- },
- ];
- this.state = {
- items: this.items,
- type: 'left',
- };
- }
-
- switch = () => {
- this.setState(state => ({
- items: state.items.length ? [] : this.items,
- }));
- }
- remove = () => {
- console.log('remove: 1');
- this.setState({ items: [] }, () => {
- console.log('remove: 2');
- this.setState({ items: [] }, () => {
- console.log('remove: 3');
- this.setState({ items: [] });
- });
- });
- }
- exchange = () => {
- console.log('exchange: 1');
- this.setState({
- items: [{
- children: '依次进入1',
- key: 1,
- }, {
- children: '依次进入2',
- key: 2,
- }],
- }, () => {
- console.log('exchange: 2');
- this.setState({
- items: [{
- children: '依次进入1',
- key: 1,
- }, {
- children: '依次进入2',
- key: 2,
- }],
- }, () => {
- console.log('exchange: 3');
- this.setState({
- items: [{
- children: '依次进入1',
- key: 1,
- }, {
- children: '依次进入2',
- key: 2,
- }],
- });
- });
- });
- }
-
- render() {
- return (
-
-
-
-
-
- {this.state.items.map((item) => )}
-
-
- );
- }
-}
-
-ReactDom.render(, document.getElementById('__react-content'));
diff --git a/examples/dynamic.html b/examples/dynamic.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/dynamic.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/dynamic.js b/examples/dynamic.js
deleted file mode 100755
index ce3023d..0000000
--- a/examples/dynamic.js
+++ /dev/null
@@ -1,120 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-class App extends React.Component {
- constructor(props) {
- super(props);
- this.index = 100;
- this.state = {
- items: [{
- children: '依次进入1',
- key: 1,
- }, {
- children: '依次进入2',
- key: 2,
- }, {
- children: '依次进入3',
- key: 3,
- }, {
- children: '依次进入4',
- key: 4,
- }, {
- children: '依次进入5',
- key: 5,
- }, {
- children: '依次进入6',
- key: 6,
- }],
- type: 'left',
- };
- }
-
- add = () => {
- const items = this.state.items;
- items.push({
- children: '新节点',
- key: this.index++,
- });
- this.setState({ items });
- }
- addTwo = () => {
- const items = this.state.items;
- items.push({
- children: '新节点',
- key: this.index++,
- });
- items.push({
- children: '新节点',
- key: this.index++,
- });
- this.setState({ items });
- }
- remove = (key, e) => {
- e.preventDefault();
- const items = this.state.items;
- const target = items.filter(item => item.key === key);
- let index;
- if (target && target[0]) {
- index = items.indexOf(target[0]);
- }
- if (index >= 0) {
- items.splice(index, 1);
- }
- this.setState({ items });
- }
- removeAll = () => {
- this.setState({
- items: [],
- });
- }
- removeAndAdd = () => {
- const items = this.state.items;
- items.splice(items.length - 1, 1);
- items.push({
- children: `新节点${Date.now()}`,
- key: this.index++,
- });
- this.setState({ items });
- }
- removeAndAddTow = () => {
- const items = this.state.items;
- items.splice(items.length - 1, 1);
- items.splice(items.length - 2, 1);
- items.push({
- children: `新节点${Date.now()}`,
- key: this.index++,
- });
- items.unshift({
- children: `新节点${Date.now()}-top`,
- key: this.index++,
- });
- this.setState({ items });
- }
- removeTwo = () => {
- const items = this.state.items;
- items.splice(1, 1);
- this.setState({ items });
- }
-
- render() {
- return (
-
-
-
-
-
-
-
-
- {this.state.items.map((item) => )}
-
-
- );
- }
-}
-
-ReactDom.render(, document.getElementById('__react-content'));
diff --git a/examples/enter-leave.html b/examples/enter-leave.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/enter-leave.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/enter-leave.js b/examples/enter-leave.js
deleted file mode 100644
index a907d2e..0000000
--- a/examples/enter-leave.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-class App extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- items: [{
- children: '依次进入1',
- key: 1,
- }, {
- children: '依次进入2',
- key: 2,
- }, {
- children: '依次进入3',
- key: 3,
- }, {
- children: '依次进入4',
- key: 4,
- }, {
- children: '依次进入5',
- key: 5,
- }, {
- children: '依次进入6',
- key: 6,
- }],
- };
- }
-
- removeAll = () => {
- this.setState({
- items: [],
- });
- }
-
- render() {
- return (
-
-
- {this.state.items.map((item) =>
- {item.children}
-
)}
-
-
-
- );
- }
-}
-
-ReactDom.render(, document.getElementById('__react-content'));
diff --git a/examples/monkey-click.html b/examples/monkey-click.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/monkey-click.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/monkey-click.js b/examples/monkey-click.js
deleted file mode 100644
index dcff407..0000000
--- a/examples/monkey-click.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-class App extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- show: true,
- items: [{
- children: '依次进入1',
- key: 1,
- }, {
- children: '依次进入2',
- key: 2,
- }, {
- children: '依次进入3',
- key: 3,
- }, {
- children: '依次进入4',
- key: 4,
- }, {
- children: '依次进入5',
- key: 5,
- }, {
- children: '依次进入6',
- key: 6,
- }],
- };
- }
-
- toggle = () => {
- this.setState({
- show: !this.state.show,
- });
- }
-
- render() {
- return (
-
-
-
{this.state.show ? '显示' : '隐藏'}
-
- {this.state.show ? this.state.items.map((item) =>
- {item.children}
-
) : null}
-
-
- );
- }
-}
-
-ReactDom.render(, document.getElementById('__react-content'));
diff --git a/examples/nested.html b/examples/nested.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/nested.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/nested.js b/examples/nested.js
deleted file mode 100644
index ec607f9..0000000
--- a/examples/nested.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-ReactDom.render(
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-
- 依次进入
- 依次进入
- 依次进入
-
- 依次进入
- 依次进入
- 依次进入
-
-
-, document.getElementById('__react-content'));
diff --git a/examples/param-func.html b/examples/param-func.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/param-func.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/param-func.js b/examples/param-func.js
deleted file mode 100644
index 29311fa..0000000
--- a/examples/param-func.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-class Page1 extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- show: true,
- };
- }
-
- onClick = () => {
- this.setState({
- show: !this.state.show,
- });
- }
- animConfigFunc = (e) => {
- if (e.key === '3') {
- return { opacity: [1, 0], translateX: [0, 30] };
- }
- return [{ opacity: [1, 0], translateX: [0, -30] }, { opacity: [1, 0], translateX: [0, 30] }];
- }
- durationFunc = (e) => {
- if (e.key === '3') {
- return [1500, 4000];
- }
- return 500;
- }
- easeFunc = (e) => {
- if (e.key === '3') {
- return ['easeOutBack', 'easeInBack'];
- }
- return 'easeInOutQuart';
- }
- delayFunc = (e) => {
- if (e.index >= 3) {
- return [1500, 0];
- }
- return 0;
- }
- render() {
- return (
-
-
- {this.state.show ? [依次进入
,
- 依次进入
,
- 改变type
,
- 依次进入
,
- 依次进入
] : null}
-
-
- );
- }
-}
-
-ReactDom.render(, document.getElementById('__react-content'));
diff --git a/examples/router.html b/examples/router.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/router.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/shortcut.html b/examples/shortcut.html
deleted file mode 100644
index 48cdce8..0000000
--- a/examples/shortcut.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
diff --git a/examples/shortcut.js b/examples/shortcut.js
deleted file mode 100644
index 2a66d93..0000000
--- a/examples/shortcut.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-ReactDom.render(
-
left
-
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-
-
top
-
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-
-
right (default)
-
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-
-
bottom
-
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-
-
alpha
-
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-
-
scale
-
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-
-
scaleBig
-
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-
-
scaleX
-
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-
-
scaleY
-
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-
-
, document.getElementById('__react-content'));
diff --git a/examples/simple.html b/examples/simple.html
deleted file mode 100644
index b3a4252..0000000
--- a/examples/simple.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
\ No newline at end of file
diff --git a/examples/simple.js b/examples/simple.js
deleted file mode 100644
index 4918f48..0000000
--- a/examples/simple.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-ReactDom.render(
- 依次进入
- 依次进入
- 依次进入
- 依次进入
- 依次进入
-, document.getElementById('__react-content'));
diff --git a/examples/switch-forcedReplay.html b/examples/switch-forcedReplay.html
deleted file mode 100644
index b3a4252..0000000
--- a/examples/switch-forcedReplay.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
\ No newline at end of file
diff --git a/examples/switch-forcedReplay.js b/examples/switch-forcedReplay.js
deleted file mode 100644
index ea8d815..0000000
--- a/examples/switch-forcedReplay.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-import './assets/switch.less';
-
-class Demo extends React.Component {
- constructor(props) {
- super(props);
- this.childrenKey = [
- { key: 1 },
- { key: 2 },
- { key: 3 },
- { key: 4 },
- { key: 5 },
- { key: 6 },
- ];
- this.state = {
- childrenKey: this.childrenKey,
- };
- }
-
- onEnter = () => {
- this.setState({
- childrenKey: null,
- });
- };
-
- onLeave = () => {
- this.setState({
- childrenKey: this.childrenKey,
- });
- };
-
- getChildren = () => {
- return (this.state.childrenKey || []).map(item => {
- return ();
- });
- };
-
- render() {
- const childrenToRender = this.getChildren();
- return (
-
鼠标经过当前区域,再移出区域查看
-
清除所有还在动画的参素并设置切换时的初始参数
-
-
- {childrenToRender}
-
-
- {childrenToRender}
-
-
- {childrenToRender}
-
-
-
);
- }
-}
-
-ReactDom.render(, document.getElementById('__react-content'));
diff --git a/examples/switch.html b/examples/switch.html
deleted file mode 100644
index b3a4252..0000000
--- a/examples/switch.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
\ No newline at end of file
diff --git a/examples/switch.js b/examples/switch.js
deleted file mode 100644
index 111de08..0000000
--- a/examples/switch.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-import './assets/switch.less';
-
-class Demo extends React.Component {
- constructor(props) {
- super(props);
- this.childrenKey = [
- { key: 1 },
- { key: 2 },
- { key: 3 },
- { key: 4 },
- { key: 5 },
- { key: 6 },
- ];
- this.state = {
- childrenKey: this.childrenKey,
- };
- }
-
- onEnter = () => {
- this.setState({
- childrenKey: null,
- });
- };
-
- onLeave = () => {
- this.setState({
- childrenKey: this.childrenKey,
- });
- };
-
- getChildren = () => {
- return (this.state.childrenKey || []).map(item => {
- return ();
- });
- };
-
- render() {
- const childrenToRender = this.getChildren();
- return (
-
鼠标经过当前区域,再移出区域查看
-
-
- {childrenToRender}
-
-
- {childrenToRender}
-
-
- {childrenToRender}
-
-
-
);
- }
-}
-
-ReactDom.render(, document.getElementById('__react-content'));
diff --git a/examples/timeline.html b/examples/timeline.html
deleted file mode 100644
index b3a4252..0000000
--- a/examples/timeline.html
+++ /dev/null
@@ -1 +0,0 @@
-placeholder
\ No newline at end of file
diff --git a/examples/timeline.js b/examples/timeline.js
deleted file mode 100644
index a7706dc..0000000
--- a/examples/timeline.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/* eslint-disable no-console,react/no-multi-comp */
-import QueueAnim from 'rc-queue-anim';
-import React from 'react';
-import ReactDom from 'react-dom';
-
-const children = [依次进入
,
- 依次进入
,
- 依次进入
,
- 依次进入
,
-];
-class Demo extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- children,
- };
- }
-
- onClick = () => {
- if (this.state.children) {
- this.setState({ children: null });
- } else {
- this.setState({ children });
- }
- }
-
- render() {
- return (
-
-
- {this.state.children}
-
-
);
- }
-}
-ReactDom.render(, document.getElementById('__react-content'));
diff --git a/now.json b/now.json
new file mode 100644
index 0000000..d7e4fdb
--- /dev/null
+++ b/now.json
@@ -0,0 +1,11 @@
+{
+ "version": 2,
+ "name": "rc-queue-anim",
+ "builds": [
+ {
+ "src": "package.json",
+ "use": "@now/static-build",
+ "config": { "distDir": ".doc" }
+ }
+ ]
+}
diff --git a/package.json b/package.json
index a9a864d..d34b1b4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rc-queue-anim",
- "version": "1.8.5",
+ "version": "2.0.0-beta.0",
"description": "Queue animation component for react",
"keywords": [
"react",
@@ -19,7 +19,7 @@
"ant-motion"
],
"homepage": "https://github.com/react-component/queue-anim",
- "author": "afc163@gmail.com",
+ "author": "155259966@qq.com",
"repository": {
"type": "git",
"url": "https://github.com/react-component/queue-anim.git"
@@ -37,58 +37,55 @@
"licenses": "MIT",
"main": "./lib/index",
"module": "./es/index",
- "config": {
- "port": 8001,
- "entry": {
- "rc-queue-anim": [
- "./assets/index.less",
- "./src/index.js"
- ]
- }
- },
"scripts": {
- "dist": "rc-tools run dist",
- "build": "rc-tools run build",
- "gh-pages": "rc-tools run gh-pages",
- "start": "rc-tools run server",
- "compile": "rc-tools run compile --babel-runtime",
- "pub": "rc-tools run pub --babel-runtime",
- "lint": "rc-tools run lint -fix",
- "karma": "rc-test run karma",
- "saucelabs": "rc-test run saucelabs",
- "test": "rc-test run test",
- "prettier": "rc-tools run prettier",
- "chrome-test": "rc-test run chrome-test",
- "coverage": "rc-test run coverage",
- "validate": "npm ls"
+ "start": "dumi dev",
+ "docs:build": "dumi build",
+ "docs:deploy": "gh-pages -d docs-dist",
+ "compile": "father-build",
+ "deploy": "npm run docs:build && npm run docs:deploy",
+ "prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"",
+ "test": "umi-test test",
+ "test:coverage": "umi-test --coverage",
+ "prepublishOnly": "npm run compile && np --tag=beta --no-cleanup --yolo --no-publish --any-branch",
+ "lint": "eslint src/ --fix --ext .tsx,.ts",
+ "lint:tsc": "tsc -p tsconfig.json --noEmit",
+ "now-build": "npm run docs:build"
},
"devDependencies": {
- "@types/react": "^16.0.0",
- "antd": "4.x",
- "core-js": "^3.2.1",
- "expect.js": "0.3.x",
- "precommit-hook": "^3.0.0",
- "rc-dialog": "~8.4.0",
- "rc-test": "6.x",
- "rc-tools": "8.x",
- "react": "^16.0.0",
- "react-dom": "^16.0.0",
- "react-router": "~4.3.1",
- "react-router-dom": "^4.1.0",
- "react-test-renderer": "^16.0.0",
- "tslint-config-prettier": "^1.17.0",
- "tslint-react": "^5.0.0",
- "typescript": "4.x"
+ "@ant-design/icons": "^4.3.0",
+ "@types/enzyme": "^3.10.5",
+ "@types/jest": "^25.2.1",
+ "@types/lodash": "^4.14.135",
+ "@types/react": "^16.8.19",
+ "@types/react-dom": "^16.8.4",
+ "@umijs/test": "^3.2.28",
+ "antd": "^4.8.4",
+ "dumi": "^1.1.19",
+ "eslint": "^7.14.0",
+ "father": "^2.22.6",
+ "father-build": "^1.18.6",
+ "gh-pages": "^3.1.0",
+ "husky": "^4.3.0",
+ "np": "^6.0.3",
+ "prettier": "^2.1.2",
+ "react": "^16.9.0",
+ "react-dom": "^16.9.0",
+ "regenerator-runtime": "^0.13.7",
+ "typescript": "^4.0.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
},
"dependencies": {
- "babel-runtime": "6.x",
- "prop-types": "^15.6.0",
- "rc-tween-one": "^2.5.0",
- "react-lifecycles-compat": "^3.0.4"
+ "@babel/runtime": "^7.11.1",
+ "tween-one": "^1.0.52"
},
- "types": "index.d.ts",
- "pre-commit": [
- "lint",
- "test"
- ]
+ "husky": {
+ "hooks": {
+ "pre-commit": [
+ "npm run lint"
+ ]
+ }
+ }
}
diff --git a/src/QueueAnim.jsx b/src/QueueAnim.jsx
deleted file mode 100644
index c54fab0..0000000
--- a/src/QueueAnim.jsx
+++ /dev/null
@@ -1,571 +0,0 @@
-import React, { createElement } from 'react';
-import PropTypes from 'prop-types';
-import TweenOne, { ticker } from 'rc-tween-one';
-import { polyfill } from 'react-lifecycles-compat';
-
-import {
- toArrayChildren,
- findChildInChildrenByKey,
- windowIsUndefined,
- mergeChildren,
- transformArguments,
- getChildrenFromProps,
-} from './utils';
-import AnimTypes from './animTypes';
-
-const noop = () => {};
-
-const typeDefault = [
- 'displayName',
- 'propTypes',
- 'getDefaultProps',
- 'defaultProps',
- 'childContextTypes',
- 'contextTypes',
- 'contextType',
-];
-
-class QueueAnim extends React.Component {
- static propTypes = {
- children: PropTypes.any,
- component: PropTypes.any,
- componentProps: PropTypes.object,
- interval: PropTypes.any,
- duration: PropTypes.any,
- delay: PropTypes.any,
- type: PropTypes.any,
- animConfig: PropTypes.any,
- ease: PropTypes.any,
- leaveReverse: PropTypes.bool,
- forcedReplay: PropTypes.bool,
- animatingClassName: PropTypes.array,
- onEnd: PropTypes.func,
- appear: PropTypes.bool,
- };
-
- static defaultProps = {
- component: 'div',
- componentProps: {},
- interval: 100,
- duration: 450,
- delay: 0,
- type: 'right',
- animConfig: null,
- ease: 'easeOutQuart',
- leaveReverse: false,
- forcedReplay: false,
- animatingClassName: ['queue-anim-entering', 'queue-anim-leaving'],
- onEnd: noop,
- appear: true,
- };
-
- static getDerivedStateFromProps(
- props,
- { prevProps, children, childrenShow: prevChildShow, $self },
- ) {
- const nextState = {
- prevProps: props,
- };
- if (prevProps && props !== prevProps) {
- const nextChildren = toArrayChildren(props.children).filter(c => c);
- let currentChildren = $self.originalChildren.filter(item => item);
- if (children.length) {
- /**
- * 多次刷新处理
- * 如果 state.children 里还有元素,元素还在动画,当前子级加回在出场的子级;
- */
- const leaveChild = children.filter(
- item => item && $self.keysToLeave.indexOf(item.key) >= 0,
- );
- $self.leaveUnfinishedChild = leaveChild
- .map(item => {
- if ($self.placeholderTimeoutIds[item.key]) {
- return item.key;
- }
- return null;
- })
- .filter(c => c);
- /**
- * 获取 leaveChild 在 state.children 里的序列,再将 leaveChild 和 currentChildren 的重新排序。
- * 避逸 state.children 在 leaveComplete 里没全部完成不触发,
- * leaveComplete 里如果动画完成了是会删除 keyToLeave,但 state.children 是在全部出场后才触发清除,
- * 所以这里需要处理出场完成的元素做清除。
- */
- const stateChildren = mergeChildren(currentChildren, children);
- const currentChild = [];
- const childReOrder = child => {
- child.forEach(item => {
- const order = stateChildren.findIndex(c => c.key === item.key);
- if (currentChild.indexOf(item) !== -1) {
- return;
- }
- // -1 不应该出现的情况,直接插入数组后面.
- if (order === -1) {
- currentChild.push(item);
- } else {
- currentChild.splice(order, 0, item);
- }
- });
- };
- childReOrder(leaveChild);
- childReOrder(currentChildren);
- currentChildren = currentChild.filter(c => c);
- }
- const newChildren = mergeChildren(currentChildren, nextChildren);
- const childrenShow = !newChildren.length ? {} : prevChildShow;
- $self.keysToEnterPaused = {};
- const emptyBool = !nextChildren.length && !currentChildren.length && children.length;
- /**
- * 在出场没结束时,childrenShow 里的值将不会清除。
- * 再触发进场时, childrenShow 里的值是保留着的, 设置了 forcedReplay 将重新播放进场。
- */
- if (!emptyBool) {
- // 空子级状态下刷新不做处理
- const nextKeys = nextChildren.map(c => c.key);
- $self.keysToLeave.forEach(key => {
- // 将所有在出场里的停止掉。避免间隔性出现
- if (nextKeys.indexOf(key) >= 0) {
- $self.keysToEnterPaused[key] = true;
- currentChildren = currentChildren.filter(item => item.key !== key);
- if (props.forcedReplay) {
- // 清掉所有出场的。
- delete childrenShow[key];
- }
- }
- });
- }
-
- $self.keysToEnter = [];
- $self.keysToLeave = [];
-
- // need render to avoid update
- nextState.childrenShow = childrenShow;
- nextState.children = newChildren;
-
- nextChildren.forEach(c => {
- if (!c) {
- return;
- }
- const key = c.key;
- const hasPrev = findChildInChildrenByKey(currentChildren, key);
- if (!hasPrev && key) {
- $self.keysToEnter.push(key);
- }
- });
-
- currentChildren.forEach(c => {
- if (!c) {
- return;
- }
- const key = c.key;
- const hasNext = findChildInChildrenByKey(nextChildren, key);
- if (!hasNext && key) {
- $self.keysToLeave.push(key);
- ticker.clear($self.placeholderTimeoutIds[key]);
- delete $self.placeholderTimeoutIds[key];
- }
- });
- }
- return nextState;
- }
- constructor(props) {
- super(props);
- /**
- * @param tweenToEnter;
- * 记录强制切换时是否需要添加 animation;
- * 如 enter 后, leave -> enter,样式是没有发生变化,就不需要添加 animation 属性。
- */
- this.tweenToEnter = {};
- /**
- * @param leaveUnfinishedChild;
- * 记录多次切换,出场没完成动画的 key。
- */
- this.leaveUnfinishedChild = [];
- /**
- * @param saveTweenOneTag;
- * 记录 TweenOne 标签,在 leaveUnfinishedChild 里使用,残留的元素不需要考虑 props 的变更。
- */
- this.saveTweenOneTag = {};
- /**
- * @param childrenShow;
- * 记录 animation 里是否需要 startAnim;
- * 当前元素是否处在显示状态
- * enterBegin 到 leaveComplete 之前都处于显示状态
- */
- this.childrenShow = {};
- /**
- * @param keysToEnter;
- * 记录进场的 key;
- */
- this.keysToEnter = [];
- /**
- * @param keysToLeave;
- * 记录出场的 key;
- */
- this.keysToLeave = [];
- /**
- * @param keysToEnterPaused;
- * 记录在进入时是否处理暂停状态
- */
- this.keysToEnterPaused = {};
- /**
- * @param placeholderTimeoutIds;
- * 进场时 deley 的 timeout 记录;
- */
- this.placeholderTimeoutIds = {};
- /**
- * @param childRefs;
- * 储存 children 的 ref;
- */
- this.childRefs = {};
- /**
- * @param currentRef;
- * 记录 component 是组件时的 ref;
- */
- this.currentRef = null;
- // 第一次进入,默认进场
- const children = toArrayChildren(getChildrenFromProps(props));
- const childrenShow = {};
- children.forEach(child => {
- if (!child || !child.key) {
- return;
- }
- if (this.props.appear) {
- this.keysToEnter.push(child.key);
- } else {
- childrenShow[child.key] = true;
- this.tweenToEnter[child.key] = true;
- }
- });
- this.originalChildren = toArrayChildren(getChildrenFromProps(props));
- this.state = {
- children,
- childrenShow,
- $self: this,
- };
- }
-
- componentDidMount() {
- if (this.props.appear) {
- this.componentDidUpdate();
- }
- }
-
- componentDidUpdate() {
- this.originalChildren = toArrayChildren(getChildrenFromProps(this.props));
- const keysToEnter = [...this.keysToEnter];
- const keysToLeave = [...this.keysToLeave];
- keysToEnter.forEach(this.performEnter);
- keysToLeave.forEach(this.performLeave);
- }
-
- componentWillUnmount() {
- Object.keys(this.placeholderTimeoutIds).forEach(key => {
- ticker.clear(this.placeholderTimeoutIds[key]);
- });
- this.keysToEnter = [];
- this.keysToLeave = [];
- this.childrenShow = {};
- }
-
- getTweenType(type, num) {
- const data = AnimTypes[type];
- return this.getTweenAnimConfig(data, num);
- }
-
- getTweenSingleConfig = (data, num, enterOrLeave) => {
- const obj = {};
- Object.keys(data).forEach(key => {
- if (Array.isArray(data[key])) {
- obj[key] = data[key][num];
- } else if ((!enterOrLeave && !num) || (enterOrLeave && num)) {
- obj[key] = data[key];
- }
- });
- return obj;
- };
-
- getTweenAnimConfig(data, num, enterOrLeave) {
- if (Array.isArray(data)) {
- return data.map(item => {
- return this.getTweenSingleConfig(item, num, enterOrLeave);
- });
- }
- return this.getTweenSingleConfig(data, num, enterOrLeave);
- }
-
- getTweenData = (key, i, type) => {
- const props = this.props;
- const enterOrLeave = type === 'enter' ? 0 : 1;
- const start = type === 'enter' ? 1 : 0;
- const end = type === 'enter' ? 0 : 1;
- const animate = this.getAnimData(props, key, i, enterOrLeave, end);
- const startAnim =
- type === 'enter' && (props.forcedReplay || !this.childrenShow[key])
- ? this.getAnimData(props, key, i, enterOrLeave, start)
- : null;
- let ease = transformArguments(props.ease, key, i)[enterOrLeave];
- const duration = transformArguments(props.duration, key, i)[enterOrLeave];
- if (Array.isArray(ease)) {
- ease = ease.map(num => num * 100);
- ease = TweenOne.easing.path(
- `M0,100C${ease[0]},${100 - ease[1]},${ease[2]},${100 - ease[3]},100,0`,
- { lengthPixel: duration / 16.6667 },
- );
- }
- return { startAnim, animate, ease, duration, isArray: Array.isArray(animate) };
- };
-
- getTweenSingleData = (startAnim, animate, ease, duration, delay, onStart, onComplete) => {
- const startLength = Object.keys(startAnim || {}).length;
- const animation = {
- onStart,
- onComplete,
- duration,
- delay,
- ease,
- ...animate,
- };
- const startAnimate = startLength ? { duration: 0, ...startAnim } : null;
- return { animation, startAnimate };
- };
-
- getTweenEnterOrLeaveData = (key, i, delay, type) => {
- let animateData = this.getTweenData(key, i, type);
- const startAnim = animateData.startAnim;
- const animate = animateData.animate;
- const onStart = (type === 'enter' ? this.enterBegin : this.leaveBegin).bind(this, key);
- const onComplete = (type === 'enter' ? this.enterComplete : this.leaveComplete).bind(this, key);
- if (animateData.isArray) {
- const length = animate.length - 1;
- const animation = [];
- const startArray = [];
- animate.forEach((leave, ii) => {
- const start = startAnim && startAnim[ii];
- const animObj = this.getTweenSingleData(
- start,
- leave,
- animateData.ease,
- animateData.duration / length,
- !ii ? delay : 0,
- !ii ? onStart : null,
- ii === length ? onComplete : null,
- );
- animation.push(animObj.animation);
- if (animObj.startAnimate) {
- startArray.push(animObj.startAnimate);
- }
- });
- return startArray.concat(animation);
- }
- animateData = this.getTweenSingleData(
- startAnim,
- animate,
- animateData.ease,
- animateData.duration,
- delay,
- onStart,
- onComplete,
- );
- return [animateData.startAnimate, animateData.animation].filter(item => item);
- };
-
- getAnimData = (props, key, i, enterOrLeave, startOrEnd) => {
- /**
- * transformArguments 第一个为 enter, 第二个为 leave;
- * getTweenAnimConfig or getTweenType 第一个为到达的位置, 第二个为开始的位置。
- * 用 tween-one 的数组来实现老的动画逻辑。。。
- */
- return props.animConfig
- ? this.getTweenAnimConfig(
- transformArguments(props.animConfig, key, i)[enterOrLeave],
- startOrEnd,
- enterOrLeave,
- )
- : this.getTweenType(transformArguments(props.type, key, i)[enterOrLeave], startOrEnd);
- };
-
- getChildrenToRender = child => {
- const { forcedReplay, leaveReverse, delay, interval, children } = this.props;
- if (!child || !child.key) {
- return child;
- }
- const key = child.key;
- if (!this.state.childrenShow[key]) {
- return null;
- }
- let i = this.keysToLeave.indexOf(key);
- let animation;
- const isFunc = typeof child.type === 'function';
- const forcedJudg = isFunc ? {} : null;
- if (isFunc) {
- Object.keys(child.type).forEach(name => {
- if (typeDefault.indexOf(name) === -1) {
- forcedJudg[name] = child.type[name];
- }
- });
- }
- let ref = () => {
- delete this.childRefs[key];
- };
- // 处理出场
- if (i >= 0) {
- if (this.leaveUnfinishedChild.indexOf(key) >= 0) {
- return this.saveTweenOneTag[key];
- }
- const $interval = transformArguments(interval, key, i)[1];
- let $delay = transformArguments(delay, key, i)[1];
- // 减掉 leaveUnfinishedChild 里的个数,因为 leaveUnfinishedChild 是旧的出场,不应该计录在队列里。
- const order =
- (leaveReverse ? this.keysToLeave.length - i - 1 : i) - this.leaveUnfinishedChild.length;
- $delay = $interval * order + $delay;
- animation = this.getTweenEnterOrLeaveData(key, i, $delay, 'leave');
- } else {
- // 处理进场;
- i = toArrayChildren(children).findIndex(c => c && c.key === key);
- ref = c => {
- this.childRefs[key] = c && c.currentRef ? c.currentRef : c;
- };
- // appear=false 时,设定 childrenShow 和 tweenToEnter 都为 true, 这里不渲染 animation;
- if (this.tweenToEnter[key] && !forcedReplay) {
- // 如果是已进入的,将直接返回标签。。
- return createElement(TweenOne, {
- key,
- component: child.type,
- forcedJudg,
- componentProps: child.props,
- ref,
- });
- }
- if (!this.tweenToEnter[key] || forcedReplay) {
- animation = this.getTweenEnterOrLeaveData(key, i, 0, 'enter');
- }
- }
- const paused = this.keysToEnterPaused[key] && this.keysToLeave.indexOf(key) === -1;
- animation = paused ? null : animation;
- const tag = createElement(TweenOne, {
- key,
- component: child.type,
- forcedJudg,
- componentProps: child.props,
- animation,
- ref,
- });
- this.saveTweenOneTag[key] = tag;
- return tag;
- };
-
- performEnter = (key, i) => {
- const interval = transformArguments(this.props.interval, key, i)[0];
- const delay = transformArguments(this.props.delay, key, i)[0];
- this.placeholderTimeoutIds[key] = ticker.timeout(
- this.performEnterBegin.bind(this, key),
- interval * i + delay,
- );
- if (this.keysToEnter.indexOf(key) >= 0) {
- this.keysToEnter.splice(this.keysToEnter.indexOf(key), 1);
- }
- };
-
- performEnterBegin = key => {
- const childrenShow = this.state.childrenShow;
- childrenShow[key] = true;
- delete this.keysToEnterPaused[key];
- ticker.clear(this.placeholderTimeoutIds[key]);
- delete this.placeholderTimeoutIds[key];
- this.setState({ childrenShow });
- };
-
- performLeave = key => {
- ticker.clear(this.placeholderTimeoutIds[key]);
- delete this.placeholderTimeoutIds[key];
- };
-
- enterBegin = (key, e) => {
- const elem = e.target;
- const animatingClassName = this.props.animatingClassName;
- elem.className = elem.className.replace(animatingClassName[1], '');
- if (elem.className.indexOf(animatingClassName[0]) === -1) {
- elem.className = `${elem.className} ${animatingClassName[0]}`.trim();
- }
- this.childrenShow[key] = true;
- };
-
- enterComplete = (key, e) => {
- if (this.keysToEnterPaused[key] || this.keysToLeave.indexOf(key) >= 0) {
- return;
- }
- const elem = e.target;
- elem.className = elem.className.replace(this.props.animatingClassName[0], '').trim();
- this.tweenToEnter[key] = true;
- this.props.onEnd({ key, type: 'enter', target: elem });
- };
-
- leaveBegin = (key, e) => {
- const elem = e.target;
- const animatingClassName = this.props.animatingClassName;
- elem.className = elem.className.replace(animatingClassName[0], '');
- if (elem.className.indexOf(animatingClassName[1]) === -1) {
- elem.className = `${elem.className} ${animatingClassName[1]}`.trim();
- }
- delete this.tweenToEnter[key];
- };
-
- leaveComplete = (key, e) => {
- // 切换时同时触发 onComplete。 手动跳出。。。
- if (toArrayChildren(this.props.children).findIndex(c => c && c.key === key) >= 0) {
- return;
- }
- const childrenShow = this.state.childrenShow;
- delete childrenShow[key];
- delete this.saveTweenOneTag[key];
- delete this.childrenShow[key];
- if (this.keysToLeave.indexOf(key) >= 0) {
- this.keysToLeave.splice(this.keysToLeave.indexOf(key), 1);
- }
- const needLeave = this.keysToLeave.some(c => childrenShow[c]);
- if (!needLeave) {
- const currentChildren = toArrayChildren(getChildrenFromProps(this.props));
- this.setState({
- children: currentChildren,
- childrenShow,
- });
- }
- const elem = e.target;
- elem.className = elem.className.replace(this.props.animatingClassName[1], '').trim();
- this.props.onEnd({ key, type: 'leave', target: elem });
- };
-
- render() {
- const {
- component,
- componentProps,
- interval,
- duration,
- delay,
- type,
- animConfig,
- ease,
- leaveReverse,
- animatingClassName,
- forcedReplay,
- onEnd,
- appear,
- ...tagProps
- } = this.props;
- if (windowIsUndefined) {
- return createElement(component, { ...tagProps, ...componentProps }, this.props.children);
- }
- const childrenToRender = toArrayChildren(this.state.children).map(this.getChildrenToRender);
- const props = {
- ...tagProps,
- ...this.props.componentProps,
- ref: c => {
- this.currentRef = c;
- },
- };
- return createElement(this.props.component, props, childrenToRender);
- }
-}
-QueueAnim.isQueueAnim = true;
-export default polyfill(QueueAnim);
diff --git a/src/QueueAnim.tsx b/src/QueueAnim.tsx
new file mode 100644
index 0000000..2ce0b43
--- /dev/null
+++ b/src/QueueAnim.tsx
@@ -0,0 +1,447 @@
+import {
+ useRef,
+ useMemo,
+ useLayoutEffect,
+ useEffect,
+ useState,
+ createElement,
+ cloneElement,
+ forwardRef,
+} from 'react';
+import { findDOMNode } from 'react-dom';
+import TweenOne, { Ticker } from 'tween-one';
+
+import {
+ toArrayChildren,
+ findChildInChildrenByKey,
+ windowIsUndefined,
+ mergeChildren,
+ transformArguments,
+} from './utils';
+import AnimTypes from './animTypes';
+
+import type { IObject, IProps, IKeys, IQueueType } from './type';
+
+const noop = () => {};
+
+export default forwardRef((props: IProps, ref: any) => {
+ const {
+ component = 'div',
+ componentProps = {},
+ interval = 100,
+ duration = 450,
+ delay = 0,
+ type = 'right',
+ animConfig = null,
+ ease = 'easeOutQuart',
+ leaveReverse = false,
+ forcedReplay = false,
+ animatingClassName = ['queue-anim-entering', 'queue-anim-leaving'],
+ onEnd = noop,
+ appear = true,
+ ...tagProps
+ } = props;
+
+ /**
+ * @param childrenShow;
+ * 记录 animation 里是否需要 startAnim;
+ * 当前元素是否处在显示状态
+ * enterBegin 到 leaveComplete 之前都处于显示状态
+ */
+ const childrenShow = useRef({});
+
+ /**
+ * @param keysToEnter;
+ * 记录进场的 key;
+ */
+ const keysToEnter = useRef([]);
+ const recordKeysToEnter = useRef([]);
+ /**
+ * @param keysToLeave;
+ * 记录出场的 key;
+ */
+ const keysToLeave = useRef([]);
+ const recordKeysToLeave = useRef([]);
+
+ /**
+ * @param placeholderTimeoutIds;
+ * 进场时 deley 的 timeout 记录;
+ */
+ const placeholderTimeoutIds = useRef({});
+ /**
+ * @param childRefs;
+ * 储存 children 的 ref;
+ */
+ const childRefs = useRef({});
+ /**
+ * @param recordAnimKeys;
+ * 记录启动动画 key
+ */
+ const recordAnimKeys = useRef({});
+
+ /**
+ * @param recordAnimKeys;
+ * 记录启动动画 key
+ */
+ const recordTweenKeys = useRef({});
+
+ /**
+ * @param oneEnterBool
+ * 记录第一次进入
+ */
+ const oneEnterBool = useRef(false);
+
+ const originalChildren = useRef([]);
+
+ const [child, setChild] = useState();
+ const [childShow, setChildShow] = useState({});
+
+ const getTweenSingleConfig = (data: any, num: number, enterOrLeave?: 0 | 1) => {
+ const obj: IObject = {};
+ Object.keys(data).forEach((key) => {
+ if (Array.isArray(data[key])) {
+ obj[key] = data[key][num];
+ } else if ((!enterOrLeave && !num) || (enterOrLeave && num)) {
+ obj[key] = data[key];
+ }
+ });
+ return obj;
+ };
+
+ const getTweenAnimConfig = (data: any, num: number, enterOrLeave?: 0 | 1) => {
+ if (Array.isArray(data)) {
+ return data.map((item) => {
+ return getTweenSingleConfig(item, num, enterOrLeave);
+ });
+ }
+ return getTweenSingleConfig(data, num, enterOrLeave);
+ };
+
+ const getTweenType = ($type: IQueueType, num: number) => {
+ const data = AnimTypes[$type];
+ return getTweenAnimConfig(data, num);
+ };
+
+ const getAnimData = (key: string | number, i: number, enterOrLeave: 0 | 1, startOrEnd: 0 | 1) =>
+ /**
+ * transformArguments 第一个为 enter, 第二个为 leave;
+ * getTweenAnimConfig or getTweenType 第一个为到达的位置, 第二个为开始的位置。
+ * 用 tween-one 的数组来实现老的动画逻辑。。。
+ */
+ animConfig
+ ? getTweenAnimConfig(
+ transformArguments(animConfig, key, i)[enterOrLeave],
+ startOrEnd,
+ enterOrLeave,
+ )
+ : getTweenType(transformArguments(type, key, i)[enterOrLeave], startOrEnd);
+
+ const getTweenData = (key: string | number, i: number, $type: string) => {
+ const enterOrLeave = $type === 'enter' ? 0 : 1;
+ const start = $type === 'enter' ? 1 : 0;
+ const end = $type === 'enter' ? 0 : 1;
+ const animate = getAnimData(key, i, enterOrLeave, end);
+ const startAnim =
+ $type === 'enter' && (forcedReplay || !childrenShow.current[key])
+ ? getAnimData(key, i, enterOrLeave, start)
+ : null;
+ let $ease = transformArguments(ease, key, i)[enterOrLeave];
+ const $duration = transformArguments(duration, key, i)[enterOrLeave];
+ if (Array.isArray(ease) && (ease.length > 2 || Array.isArray(ease[0]))) {
+ $ease = $ease.map((num: number) => num * 100);
+ $ease = `M0,100C${$ease[0]},${100 - $ease[1]},${$ease[2]},${100 - $ease[3]},100,0`;
+ }
+ return {
+ startAnim,
+ animate,
+ ease: $ease,
+ duration: $duration,
+ };
+ };
+
+ const enterBegin = (key: string | number, e: any) => {
+ const elem = e.targets;
+ elem.className = elem.className.replace(animatingClassName[1], '');
+ if (elem.className.indexOf(animatingClassName[0]) === -1) {
+ elem.className = `${elem.className} ${animatingClassName[0]}`.trim();
+ }
+ if (keysToEnter.current.indexOf(key) >= 0) {
+ keysToEnter.current.splice(keysToEnter.current.indexOf(key), 1);
+ }
+ childrenShow.current[key] = true;
+ };
+ const enterComplete = (key: string | number, e: any) => {
+ if (keysToLeave.current.indexOf(key) >= 0) {
+ return;
+ }
+ const elem = e.targets;
+ elem.className = elem.className.replace(animatingClassName[0], '').trim();
+ delete recordTweenKeys.current[key];
+ onEnd({ key, type: 'enter', target: elem });
+ };
+
+ const leaveBegin = (key: string | number, e: any) => {
+ const elem = e.targets;
+ elem.className = elem.className.replace(animatingClassName[0], '');
+ if (elem.className.indexOf(animatingClassName[1]) === -1) {
+ elem.className = `${elem.className} ${animatingClassName[1]}`.trim();
+ }
+ };
+
+ const leaveComplete = (key: string | number, e: any) => {
+ // 切换时同时触发 onComplete。 手动跳出。。。
+ toArrayChildren(props.children).findIndex((c) => c && c.key === key);
+ if (toArrayChildren(props.children).findIndex((c) => c && c.key === key) >= 0) {
+ return;
+ }
+ delete childrenShow.current[key];
+ delete recordTweenKeys.current[key];
+ originalChildren.current = originalChildren.current.filter((c) => c.key !== key);
+ // 这里不用启动动画,,直接删;
+ if (keysToLeave.current.indexOf(key) >= 0) {
+ keysToLeave.current.splice(keysToLeave.current.indexOf(key), 1);
+ }
+ const needLeave = keysToLeave.current.some((c) => childShow[c]);
+
+ if (!needLeave) {
+ const currentChildren = toArrayChildren(props.children);
+ setChild(currentChildren);
+ setChildShow({ ...childrenShow.current });
+ recordKeysToLeave.current.forEach((k) => {
+ delete recordAnimKeys.current[k];
+ });
+ }
+ const elem = e.targets;
+ elem.className = elem.className.replace(animatingClassName[1], '').trim();
+ onEnd({ key, type: 'leave', target: elem });
+ };
+
+ const performEnterBegin = (key: string | number) => {
+ childShow[key] = true;
+ Ticker.clear(placeholderTimeoutIds.current[key]);
+ delete placeholderTimeoutIds.current[key];
+ setChildShow({ ...childShow });
+ };
+
+ const performEnter = (key: string | number, i: number) => {
+ const $interval = transformArguments(interval, key, i)[0];
+ const $delay = transformArguments(delay, key, i)[0];
+ placeholderTimeoutIds.current[key] = Ticker.timeout(() => {
+ performEnterBegin(key);
+ }, $interval * i + $delay);
+ };
+
+ const performLeave = (key: string | number) => {
+ Ticker.clear(placeholderTimeoutIds.current[key]);
+ delete placeholderTimeoutIds.current[key];
+ };
+
+ const getTweenOneEnterOrLeave = (
+ key: string | number,
+ i: number,
+ $delay: number,
+ $type: string,
+ ) => {
+ const animateData = getTweenData(key, i, $type);
+ const onStart = (e: any) => {
+ ($type === 'enter' ? enterBegin : leaveBegin)(key, e);
+ };
+ const onComplete = (e: any) => {
+ ($type === 'enter' ? enterComplete : leaveComplete)(key, e);
+ };
+ if (Array.isArray(animateData.animate)) {
+ const length = animateData.animate.length - 1;
+ const animation = animateData.animate.map((item, ii) => {
+ return {
+ ...item,
+ startAt: animateData.startAnim ? animateData.startAnim[ii] : undefined,
+ duration: animateData.duration / length,
+ delay: !ii && $type === 'leave' ? $delay : 0,
+ onStart: !ii ? onStart : undefined,
+ onComplete: ii === length ? onComplete : undefined,
+ };
+ });
+ return animation;
+ }
+ return {
+ ...animateData.animate,
+ startAt: animateData.startAnim || undefined,
+ ease: animateData.ease,
+ duration: animateData.duration,
+ onStart,
+ onComplete,
+ delay: $delay,
+ };
+ };
+ useEffect(
+ () => () => {
+ Object.keys(recordTweenKeys.current).forEach((key) => {
+ const tween = recordTweenKeys.current[key];
+ if (!tween) {
+ return;
+ }
+ tween.kill();
+ });
+ },
+ [],
+ );
+ useEffect(() => {
+ const nextChildren = toArrayChildren(props.children).filter((c) => c);
+ const currentChildren = originalChildren.current.filter((item) => item);
+ const newChildren = mergeChildren(currentChildren, nextChildren);
+ const $keysToEnter: IKeys = [];
+ const $keysToLeave: IKeys = [];
+ if (!appear && !oneEnterBool.current) {
+ const $childShow: IObject = {};
+ newChildren.forEach((c: any) => {
+ if (!c || !c.key) {
+ return;
+ }
+ $childShow[c.key] = true;
+ });
+ originalChildren.current = newChildren;
+ childrenShow.current = { ...$childShow };
+ setChildShow($childShow);
+ } else {
+ // console.log(nextChildren, recordAnimKeys.current, keysToEnter.current, keysToLeave.current);
+ currentChildren.forEach((c) => {
+ if (!c) {
+ return;
+ }
+ const { key } = c;
+ const hasNext = findChildInChildrenByKey(nextChildren, key);
+ if (!hasNext && key) {
+ $keysToLeave.push(key);
+ Ticker.clear(placeholderTimeoutIds.current[key]);
+ delete placeholderTimeoutIds.current[key];
+ }
+ });
+
+ nextChildren.forEach((c: any) => {
+ if (!c) {
+ return;
+ }
+
+ const { key } = c;
+ const hasPrev = findChildInChildrenByKey(currentChildren, key);
+ // 如果 nextChildren 和当前的一致,且动画里是出场,改回进场;
+ if (
+ (!hasPrev && key) ||
+ ((!recordAnimKeys.current[key] ||
+ recordAnimKeys.current[key] === 'leave' ||
+ keysToEnter.current.indexOf(key) >= 0) &&
+ $keysToLeave.indexOf(key) === -1)
+ ) {
+ $keysToEnter.push(key);
+ }
+ });
+ }
+ // console.log('child update', $keysToEnter, $keysToLeave, newChildren);
+
+ keysToEnter.current = $keysToEnter;
+ // keysToEnter 在启动时就会删除;
+ recordKeysToEnter.current = [...$keysToEnter];
+ keysToLeave.current = $keysToLeave;
+ recordKeysToLeave.current = [...$keysToLeave];
+
+ // console.log($keysToEnter, $keysToLeave);
+ setChild(newChildren);
+ }, [props.children]);
+ useLayoutEffect(() => {
+ originalChildren.current = child || [];
+ if (appear || oneEnterBool.current) {
+ const $keysToEnter = [...keysToEnter.current];
+ const $keysToLeave = [...keysToLeave.current];
+ $keysToEnter.forEach(performEnter);
+ $keysToLeave.forEach(performLeave);
+ }
+ if (child) {
+ oneEnterBool.current = true;
+ }
+ }, [child]);
+ useLayoutEffect(() => {
+ if (child) {
+ child.forEach((item) => {
+ const { key } = item;
+ const dom = childRefs.current[key];
+ if (!dom) {
+ return;
+ }
+ let animation;
+ let index = keysToLeave.current.indexOf(key); // children.findIndex(c => c.key === key);
+ const $interval = transformArguments(interval, key, index);
+ const $delay = transformArguments(delay, key, index);
+
+ // 处理出场
+ if (index >= 0) {
+ if (recordAnimKeys.current[key] === 'leave') {
+ return;
+ }
+
+ const order = leaveReverse ? keysToLeave.current.length - index - 1 : index;
+ const d = $interval[1] * order + $delay[1];
+ animation = getTweenOneEnterOrLeave(key, index, d, 'leave');
+ recordAnimKeys.current[key] = 'leave';
+ } else {
+ if (recordAnimKeys.current[key] === 'enter' || keysToEnter.current.indexOf(key) === -1) {
+ return;
+ }
+ index = recordKeysToEnter.current.indexOf(key);
+ const d = $interval[0] * index + $delay[0];
+ // console.log(recordAnimKeys.current[key], dom);
+ animation = getTweenOneEnterOrLeave(
+ key,
+ index,
+ recordAnimKeys.current[key] === 'leave' ? d : 0,
+ 'enter',
+ );
+ recordAnimKeys.current[key] = 'enter';
+ }
+ if (recordTweenKeys.current[key]) {
+ recordTweenKeys.current[key].kill();
+ }
+
+ if (forcedReplay) {
+ const anim = {
+ ...(Array.isArray(animation) ? animation[0].startAt : animation.startAt),
+ type: 'set',
+ };
+ TweenOne(dom, { animation: anim });
+ }
+ recordTweenKeys.current[key] = TweenOne(dom, {
+ animation,
+ });
+ });
+ }
+ }, [childShow, child]);
+
+ return useMemo(() => {
+ // console.log('--------render--------', childShow);
+ if (windowIsUndefined) {
+ return createElement(component, { ...tagProps, ...componentProps, ref });
+ }
+ const childrenToRender = toArrayChildren(child).map((item) => {
+ if (!item || !item.key) {
+ return item;
+ }
+ return (
+ childShow[item.key] &&
+ cloneElement(item, {
+ ref: (c: any) => {
+ childRefs.current[item.key] = c instanceof Element ? c : findDOMNode(c);
+ if (!c) {
+ delete childRefs.current[item.key];
+ }
+ },
+ key: item.key,
+ })
+ );
+ });
+ const p = {
+ ...tagProps,
+ ...componentProps,
+ ref,
+ };
+ return createElement(component, p, childrenToRender);
+ }, [childShow, child]);
+});
diff --git a/src/animTypes.js b/src/animTypes.ts
similarity index 100%
rename from src/animTypes.js
rename to src/animTypes.ts
diff --git a/src/index.js b/src/index.tsx
similarity index 78%
rename from src/index.js
rename to src/index.tsx
index 8d9810a..df6b330 100644
--- a/src/index.js
+++ b/src/index.tsx
@@ -2,3 +2,5 @@
import QueueAnim from './QueueAnim';
export default QueueAnim;
+
+export * from './type';
diff --git a/src/type.ts b/src/type.ts
new file mode 100644
index 0000000..3361153
--- /dev/null
+++ b/src/type.ts
@@ -0,0 +1,59 @@
+import type { IObject as IObj } from 'tween-one/lib/typings';
+import type { IEaseType as IEase } from 'tween-one/lib/typings/IAnimObject';
+import type React from 'react';
+
+export type IObject = IObj;
+
+export type IKeys = (string | number)[];
+
+export type IQueueType =
+ | 'alpha'
+ | 'left'
+ | 'right'
+ | 'top'
+ | 'bottom'
+ | 'scale'
+ | 'scaleBig'
+ | 'scaleX'
+ | 'scaleY';
+export type INumberOrArrayOrFunc =
+ | number
+ | [number, number]
+ | ((e: { key: string; index: number }) => number | number[]);
+export type IEaseType = IEase | [number, number, number, number];
+
+export type IQueueTypeOrArrayOrFunc =
+ | IQueueType
+ | [IQueueType, IQueueType]
+ | ((e: { key: string; index: number }) => IQueueType | [IQueueType, IQueueType]);
+export type IEaseTypeOrArrayOrFunc =
+ | IEaseType
+ | IEaseType[]
+ | ((e: { key: string; index: number }) => IEaseType | IEaseType[]);
+export type IAnimConfigOrArrayOrFunc =
+ | {}
+ | [{}]
+ | ((e: { key: string; index: number }) => {} | {}[]);
+
+interface AllHTMLAttributes
+ extends Omit, 'crossOrigin'>,
+ React.AllHTMLAttributes {}
+export interface IProps extends Omit {
+ type?: IQueueTypeOrArrayOrFunc;
+ animConfig?: IAnimConfigOrArrayOrFunc;
+ delay?: INumberOrArrayOrFunc;
+ duration?: INumberOrArrayOrFunc;
+ interval?: INumberOrArrayOrFunc;
+ leaveReverse?: boolean;
+ ease?: IEaseTypeOrArrayOrFunc;
+ appear?: boolean;
+ component?:
+ | string
+ | React.ClassType>
+ | React.ForwardRefExoticComponent }>
+ | undefined;
+ componentProps?: IObject;
+ animatingClassName?: string[];
+ forcedReplay?: boolean;
+ onEnd?: (e: { key: string | number; type: string; target: HTMLElement }) => void;
+}
diff --git a/src/utils.js b/src/utils.ts
similarity index 70%
rename from src/utils.js
rename to src/utils.ts
index 1643704..696798e 100644
--- a/src/utils.js
+++ b/src/utils.ts
@@ -1,4 +1,5 @@
/* eslint no-prototype-builtins: 0 */
+import type { IObject } from './type';
import React from 'react';
export const windowIsUndefined = !(
@@ -7,18 +8,18 @@ export const windowIsUndefined = !(
window.document.createElement
);
-export function toArrayChildren(children) {
- const ret = [];
- React.Children.forEach(children, c => {
+export function toArrayChildren(children: any) {
+ const ret: any[] = [];
+ React.Children.forEach(children, (c) => {
ret.push(c);
});
return ret;
}
-export function findChildInChildrenByKey(children, key) {
- let ret = null;
+export function findChildInChildrenByKey(children: any[], key: string) {
+ let ret: any = null;
if (children) {
- children.forEach(c => {
+ children.forEach((c) => {
if (ret || !c) {
return;
}
@@ -30,14 +31,14 @@ export function findChildInChildrenByKey(children, key) {
return ret;
}
-export function mergeChildren(prev, next) {
- let ret = [];
+export function mergeChildren(prev: any, next: any) {
+ let ret: any = [];
// For each key of `next`, the list of keys to insert before that key in
// the combined list
- const nextChildrenPending = {};
- let pendingChildren = [];
- let followChildrenKey;
- prev.forEach(c => {
+ const nextChildrenPending: IObject = {};
+ let pendingChildren: any = [];
+ let followChildrenKey: any;
+ prev.forEach((c: any) => {
if (!c) {
return;
}
@@ -54,7 +55,7 @@ export function mergeChildren(prev, next) {
if (!followChildrenKey) {
ret = ret.concat(pendingChildren);
}
- next.forEach(c => {
+ next.forEach((c: any) => {
if (!c) {
return;
}
@@ -70,7 +71,7 @@ export function mergeChildren(prev, next) {
return ret;
}
-export function transformArguments(arg, key, i) {
+export function transformArguments(arg: any, key: string | number, i: number) {
let result;
if (typeof arg === 'function') {
result = arg({
@@ -88,7 +89,3 @@ export function transformArguments(arg, key, i) {
}
return [result, result];
}
-
-export function getChildrenFromProps(props) {
- return props && props.children;
-}
diff --git a/tests/index.test.jsx b/tests/index.test.jsx
new file mode 100644
index 0000000..610f076
--- /dev/null
+++ b/tests/index.test.jsx
@@ -0,0 +1,324 @@
+import React, { useState } from 'react';
+import { render, unmountComponentAtNode } from 'react-dom';
+import { act } from 'react-dom/test-utils';
+
+import QueueAnim from '../src';
+
+import TweenOne, { Ticker } from 'tween-one';
+
+function getOpacity(node) {
+ if (!node) {
+ return NaN;
+ }
+ // console.log('node.style.opacity)', node.style)
+ return parseFloat(node.style.opacity);
+}
+
+function getLeft(node) {
+ if (!node) {
+ return NaN;
+ }
+ return parseFloat(node.style.left);
+}
+
+function getTop(node) {
+ if (!node) {
+ return NaN;
+ }
+ return parseFloat(node.style.top);
+}
+
+let container = null;
+beforeEach(() => {
+ // 创建一个 DOM 元素作为渲染目标
+ container = document.createElement('div');
+ document.body.appendChild(container);
+
+ TweenOne({}, { x: 100, duration: 2000000 });
+ //jest.useFakeTimers();
+});
+
+afterEach(() => {
+ // 退出时进行清理
+ unmountComponentAtNode(container);
+ container.remove();
+ container = null;
+ //jest.useRealTimers();
+});
+
+const items = [
+ {
+ key: 1,
+ content: 'div',
+ },
+ {
+ key: 2,
+ content: 'div',
+ },
+ {
+ key: 3,
+ content: 'div',
+ },
+];
+
+const child = items.map((item) => (
+
+ {item.content}
+
+));
+
+const QueueAnimComp = (props) => {
+ const [children, setChildren] = useState(child);
+ const [unmount, setUnmount] = useState(false);
+ return (
+
+ {!unmount && (
+
+ {children}
+
+ )}
+
+
+
+ );
+};
+
+const interval = 100;
+
+function shouldAnimatingThisOne(children, index) {
+ children.forEach((node, i) => {
+ console.log(index, i, getOpacity(node));
+ if (i <= index) {
+ expect(getOpacity(node)).toBeGreaterThan(0);
+ } else {
+ // placeholder
+ // expect(node.innerHTML).to.be('');
+ }
+ });
+}
+
+it('should render children', (done) => {
+ act(() => {
+ render({child}, container);
+ // jest.advanceTimersByTime(50);
+ }); /* Ticker.timeout 0 会直接返回
+ let { children } = container.querySelector('[id=queue]');
+ expect(children.length).toBe(0); */
+
+ const { children } = container.querySelector('[id=queue]');
+
+ Ticker.timeout(() => {
+ // shouldAnimatingThisOne(0);
+ expect(children.length).toBe(1);
+ done();
+ });
+});
+
+it('should render all children', () => {
+ act(() => {
+ render(
+
+ {child}
+ ,
+ container,
+ );
+ });
+ const { children } = container.querySelector('[id=queue]');
+ console.log(children.length);
+ expect(children.length).toBe(3);
+});
+
+it('should have queue animation', (done) => {
+ act(() => {
+ render({child}, container);
+ });
+ const { children } = container.querySelector('[id=queue]');
+ Ticker.timeout(() => {
+ shouldAnimatingThisOne(children, 0);
+ Ticker.timeout(() => {
+ shouldAnimatingThisOne(children, 1);
+ Ticker.timeout(() => {
+ shouldAnimatingThisOne(children, 2);
+ done();
+ }, interval);
+ }, interval);
+ }, 50);
+ /* act(() => {
+ jest.advanceTimersByTime(100);
+ });
+ shouldAnimatingThisOne(1);
+*/
+});
+
+it('custom api queue animation', (done) => {
+ act(() => {
+ render(
+
+ {child}
+ ,
+ container,
+ );
+ });
+ const node = container.querySelector('[id=queue]');
+ const { children } = node;
+ console.log('component style', getTop(node));
+ expect(getTop(node)).toBe(10);
+ console.log('component tagName', node.tagName);
+ expect(node.tagName).toBe('P');
+ Ticker.timeout(() => {
+ expect(children.length).toBe(0);
+ Ticker.timeout(() => {
+ expect(children.length).toBe(1);
+ done();
+ }, 100);
+ }, 950);
+});
+
+it('custom animConfig queue animation', (done) => {
+ act(() => {
+ render(
+
+ {child}
+ ,
+ container,
+ );
+ });
+ const { children } = container.querySelector('[id=queue]');
+ Ticker.timeout(() => {
+ expect(getLeft(children[0])).toBe(100);
+ done();
+ }, 500);
+});
+
+it('custom forcedReplay queue animation', (done) => {
+ act(() => {
+ render(
+ ,
+ container,
+ );
+ });
+ const { children } = container.querySelector('[id=queue]');
+ const button = document.querySelector('[data-testId=toggle]');
+ Ticker.timeout(() => {
+ // 出场
+ act(() => {
+ button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ });
+
+ Ticker.timeout(() => {
+ // 重新进入
+ act(() => {
+ button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ });
+
+ expect(getLeft(children[0])).toBe(0);
+ done();
+ }, 500);
+ }, 500);
+});
+
+it('should support custom animation config array', (done) => {
+ act(() => {
+ render(, container);
+ });
+ const { children } = container.querySelector('[id=queue]');
+ const button = document.querySelector('[data-testId=toggle]');
+ Ticker.timeout(() => {
+ expect(getLeft(children[0])).toBe(100);
+ act(() => {
+ button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ });
+ Ticker.timeout(() => {
+ console.log('top', getTop(children[0]));
+ expect(getTop(children[0])).toBe(0);
+ done();
+ }, 500);
+ }, 500);
+});
+it('should has animating config is func enter', (done) => {
+ act(() => {
+ render(
+ {
+ if (e.index === 1) {
+ return [[{ top: [100, 0] }, { left: [100, 0]}]];
+ }
+ return { left: [100, 0] };
+ }}
+ />,
+ container,
+ );
+ });
+ const { children } = container.querySelector('[id=queue]');
+ const button = document.querySelector('[data-testId=toggle]');
+ Ticker.timeout(() => {
+ expect(getLeft(children[0])).toBe(100);
+ expect(getTop(children[1])).toBe(100);
+ done();
+ act(() => {
+ button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ });
+ Ticker.timeout(() => {
+ // expect(getLeft(children[0])).toBe(0);
+ // expect(getTop(children[1])).toBe(0);
+ done();
+ }, 600);
+ }, 600);
+});
+it('should have leave animation', (done) => {
+ act(() => {
+ render(, container);
+ });
+ const { children } = container.querySelector('[id=queue]');
+ const button = document.querySelector('[data-testId=toggle]');
+ Ticker.timeout(() => {
+ expect(children.length).toBe(3);
+ act(() => {
+ button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ });
+ Ticker.timeout(() => {
+ expect(children.length).toBe(0);
+ done();
+ }, 1000);
+ }, 1000);
+});
+
+it('should have unmount animation', (done) => {
+ act(() => {
+ render(, container);
+
+ const button = document.querySelector('[data-testId=unmount]');
+ Ticker.timeout(() => {
+ act(() => {
+ button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+ });
+ const node = container.querySelector('[id=queue]');
+ expect(node).toBe(null);
+ done();
+ }, 1000);
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
index f5622bd..bb3518e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,31 +1,30 @@
{
"compilerOptions": {
- "outDir": "build/dist",
+ "target": "esnext",
"module": "esnext",
- "target": "es2016",
- "lib": ["es6", "dom"],
- "sourceMap": true,
- "jsx": "react",
- "allowSyntheticDefaultImports": true,
"moduleResolution": "node",
- "rootDirs": ["/src", "./typings"],
- "forceConsistentCasingInFileNames": true,
- "noImplicitReturns": true,
- "suppressImplicitAnyIndexErrors": true,
- "noUnusedLocals": true,
- "allowJs": true,
- "experimentalDecorators": true
+ "importHelpers": true,
+ "jsx": "react",
+ "typeRoots": ["./typings"],
+ "declaration": true,
+ "esModuleInterop": true,
+ "sourceMap": true,
+ "baseUrl": "./",
+ "strict": true,
+ "paths": {
+ "@/*": ["src/*"],
+ "@@/*": ["src/.umi/*"],
+ "rc-queue-anim": ["src/index.tsx"]
+ },
+ "allowSyntheticDefaultImports": true
},
- "include": ["./src"],
"exclude": [
"node_modules",
- "build",
- "scripts",
- "acceptance-tests",
- "webpack",
- "jest",
- "src/setupTests.ts",
- "tslint:latest",
- "tslint-config-prettier"
+ "lib",
+ "es",
+ "dist",
+ "typings",
+ "**/__test__",
+ "test"
]
}
diff --git a/tslint.json b/tslint.json
deleted file mode 100644
index 125e217..0000000
--- a/tslint.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "extends": ["tslint:latest", "tslint-react", "tslint-config-prettier"],
- "rules": {
- "no-var-requires": false,
- "no-submodule-imports": false,
- "object-literal-sort-keys": false,
- "jsx-no-lambda": false,
- "no-implicit-dependencies": false,
- "no-console": false
- }
-}
diff --git a/typings/global/import.d.ts b/typings/global/import.d.ts
new file mode 100644
index 0000000..1b73c18
--- /dev/null
+++ b/typings/global/import.d.ts
@@ -0,0 +1,6 @@
+import * as CSS from 'csstype';
+declare module 'csstype' {
+ interface Properties {
+ [key: string]: any;
+ }
+}
\ No newline at end of file
diff --git a/typings/global/index.d.ts b/typings/global/index.d.ts
new file mode 100644
index 0000000..6c0343c
--- /dev/null
+++ b/typings/global/index.d.ts
@@ -0,0 +1,7 @@
+///
+declare module '*.css';
+declare module '*.less';
+declare module 'style-utils';
+declare module 'tween-functions';
+declare module 'raf';
+declare module 'flubber';
\ No newline at end of file