├── .editorconfig ├── .gitignore ├── .jshintignore ├── .jshintrc ├── .prettierrc ├── .travis.yml ├── HISTORY.md ├── LICENSE.md ├── README.md ├── assets └── index.less ├── eslintrc.js ├── examples ├── assets │ ├── arrow.less │ ├── bgParallax.less │ ├── index.less │ └── thumb.less ├── autoPlayEffect.html ├── autoPlayEffect.js ├── autoplay.html ├── autoplay.js ├── bgParallax.html ├── bgParallax.js ├── change.html ├── change.js ├── customAnimType.html ├── customAnimType.js ├── customArrow.html ├── customArrow.js ├── customThumb.html ├── customThumb.js ├── followMouse.html ├── followMouse.js ├── leaveChildHide.html ├── leaveChildHide.js ├── parallax.html ├── parallax.js ├── simple.html ├── simple.js ├── thumbBottom.html ├── thumbBottom.js ├── videoBg.html └── videoBg.js ├── package.json ├── src ├── Arrow.jsx ├── BannerAnim.jsx ├── BgElement.jsx ├── Element.jsx ├── Thumb.jsx ├── anim.jsx ├── index.js └── utils.js ├── tests ├── index.js └── test.tsx ├── tsconfig.json ├── tslint.json └── typings ├── Arrow.d.ts ├── BgElement.d.ts ├── Element.d.ts ├── Thumb.d.ts └── index.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*.{js,css}] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.log 3 | .idea 4 | .ipr 5 | .iws 6 | *~ 7 | ~* 8 | *.diff 9 | *.patch 10 | *.bak 11 | .DS_Store 12 | Thumbs.db 13 | .project 14 | .*proj 15 | .svn 16 | *.swp 17 | *.swo 18 | *.pyc 19 | *.pyo 20 | node_modules 21 | .cache 22 | *.css 23 | build 24 | lib 25 | dist 26 | coverage -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | 4 | "curly": true, 5 | "latedef": true, 6 | "quotmark": true, 7 | "undef": true, 8 | "unused": true, 9 | "trailing": true 10 | } 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 100, 5 | "overrides": [ 6 | { 7 | "files": ".prettierrc", 8 | "options": { "parser": "json" } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | sudo: false 4 | 5 | notifications: 6 | email: 7 | - 155259966@qq.com 8 | 9 | node_js: 10 | - 8.1.0 11 | 12 | before_install: 13 | - | 14 | if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qvE '(\.md$)|(^(docs|examples))/' 15 | then 16 | echo "Only docs were updated, stopping build process." 17 | exit 18 | fi 19 | npm install npm@3.x -g 20 | phantomjs --version 21 | script: 22 | - | 23 | if [ "$TEST_TYPE" = test ]; then 24 | npm test 25 | else 26 | npm run $TEST_TYPE 27 | fi 28 | env: 29 | matrix: 30 | - TEST_TYPE=lint 31 | - TEST_TYPE=test 32 | - TEST_TYPE=coverage 33 | 34 | 35 | matrix: 36 | allow_failures: 37 | - env: "TEST_TYPE=saucelabs" -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # History 2 | ---- 3 | 4 | ## 1.0.0 5 | 1. 更新 react 版本到 16 6 | 2. 更新 `setAnimCompToTagComp` 方法以 dom 的全局属性来转换掉动画组件。 7 | 3. 去除 hideProps, 替换为 leaveChildHide. 8 | 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2015-present Alipay.com, https://www.alipay.com/ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rc-banner-anim 2 | --- 3 | 4 | React BannerAnim Component 5 | 6 | 7 | [![NPM version][npm-image]][npm-url] 8 | [![build status][travis-image]][travis-url] 9 | [![Test coverage][coveralls-image]][coveralls-url] 10 | [![node version][node-image]][node-url] 11 | [![npm download][download-image]][download-url] 12 | 13 | [npm-image]: http://img.shields.io/npm/v/rc-banner-anim.svg?style=flat-square 14 | [npm-url]: http://npmjs.org/package/rc-banner-anim 15 | [travis-image]: https://img.shields.io/travis/react-component/banner-anim.svg?style=flat-square 16 | [travis-url]: https://travis-ci.org/react-component/banner-anim 17 | [coveralls-image]: https://img.shields.io/coveralls/react-component/banner-anim.svg?style=flat-square 18 | [coveralls-url]: https://coveralls.io/r/react-component/banner-anim?branch=master 19 | [gemnasium-image]: http://img.shields.io/gemnasium/react-component/banner-anim.svg?style=flat-square 20 | [node-image]: https://img.shields.io/badge/node.js-%3E=_0.10-green.svg?style=flat-square 21 | [node-url]: http://nodejs.org/download/ 22 | [download-image]: https://img.shields.io/npm/dm/rc-banner-anim.svg?style=flat-square 23 | [download-url]: https://npmjs.org/package/rc-banner-anim 24 | 25 | 26 | ## Browser Support 27 | 28 | |![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)| 29 | | --- | --- | --- | --- | --- | 30 | | IE 10+ ✔ | Chrome 31.0+ ✔ | Firefox 31.0+ ✔ | Opera 30.0+ ✔ | Safari 7.0+ ✔ | 31 | 32 | 33 | ## Development 34 | 35 | ``` 36 | npm install 37 | npm start 38 | ``` 39 | 40 | ## Example 41 | 42 | http://localhost:8012/examples/ 43 | 44 | 45 | online example: http://react-component.github.io/banner-anim/ 46 | 47 | 48 | ## install 49 | 50 | 51 | [![rc-banner-anim](https://nodei.co/npm/rc-banner-anim.png)](https://npmjs.org/package/rc-banner-anim) 52 | 53 | 54 | ## Usage 55 | 56 | ```js 57 | var BannerAnim = require('rc-banner-anim'); 58 | var React = require('react'); 59 | const { Element } = BannerAnim; 60 | const BgElement = Element.BgElement; 61 | React.render( 62 | 63 | 64 | test text 65 | 66 | 67 | 68 | test text 69 | 70 | , container); 71 | ``` 72 | 73 | ## API 74 | 75 | ### BannerAnim 76 | 77 | ``` 78 | `ref` control jump: { this.banner = c; }}/> 79 | 80 | prev: this.banner.prev(); 81 | 82 | next: this.banner.next(); 83 | 84 | jump: this.banner.slickGoTo(number); number from 0; 85 | ``` 86 | 87 | | name | type | default | description | 88 | |----------|-----------------|--------------|-----------------------| 89 | | type | string / array | All animType | Provide `across`, `vertical`, `acrossOverlay`, `verticalOverlay`, (`gridBar`, `grid`) => duration is a single block of animation time, video bg no use | 90 | | duration | number | 450 | Single switch time. | 91 | | delay | number | 0 | switch delay. | 92 | | ease | string | `easeInOutQuad` | easing. | 93 | | initShow | number | 0 | start show | 94 | | arrow | boolean | `true` | `Arrow` is children, this is null and void. else is default arrow | 95 | | thumb | boolean | `true` | ^ | 96 | | autoPlay | boolean | `false` | auto play | 97 | | autoPlaySpeed | number | 5000 | auto play delay | 98 | | autoPlayEffect | boolean | `true` | auto play when mouse leave | 99 | | onChange | func | - | onChange(`before` or `after`, currentShowInt) | 100 | | prefixCls | string | - | user class | 101 | | children | react.component| - | `Element`(must), `Arrow`, `Thumb` | 102 | | sync | boolean | false | `Element` the children and `Element` the same time animation | 103 | | dragPlay | boolean | true | drag play next or prev | 104 | | component | string | 'div' | component tag | 105 | 106 | ### Element 107 | 108 | > children is `TweenOne`, animation type must `from`; 109 | 110 | | name | type | default | description | 111 | |----------|-----------------|--------------|-----------------------| 112 | | leaveChildHide | boolean | false | children leave switch animation. Replace the `hideProps`.| 113 | | sync | boolean | false | children and `Element` the same time animation | 114 | | prefixCls | string | - | user class | 115 | | followParallax | object | null | follow mouse anim | 116 | | component | string | 'div' | component tag | 117 | | componentProps | object | null | component is React.Element, component tag props, not add `style` | 118 | 119 | #### followParallax is object 120 | | name | type | default | description | 121 | |----------|-----------------|--------------|-----------------------| 122 | | delay | number | null | open followParallax delay | 123 | | data | array | null | content: { key: string, value: number, type: array or string, bgPosition: string }; key: children key; value: animation interval value, example: value is 20 => [left: -20, center: 0 , right: 20] ; type: style or `x` `y`, bgPosition: if type is backgroundPosition, this is bg default position, else is invalid. | 124 | | ease | string | `easeOutQuad` | animate ease. [refer](http://easings.net/en) | 125 | | minMove | number | null | ease.easeInOutQuad(start, minMove, 1, end); The mouse to move once, the minimum point of moving graphics, a second to reach the position of the mouse. | 126 | 127 | ### Element.BgElement 128 | 129 | | name | type | default | description | 130 | |----------|-----------------|--------------|-----------------------| 131 | | className | string | - | className | 132 | | scrollParallax | object | null | { y: 100 }, from bottom to top of browser, element leave display area y is 100 | 133 | | videoResize | boolean | true | children is video, video follow window resize | 134 | | component | string | 'div' | component tag | 135 | | componentProps | object | null | component is React.Element, component tag props, not add `style` | 136 | 137 | ### Arrow 138 | 139 | | name | type | default | description | 140 | |----------|-----------------|--------------|-----------------------| 141 | | arrowType | 'prev' \| 'next' | - | arrow type | 142 | | prefixCls | string | - | user class | 143 | | component | string / React.Element | `div` | component tag | 144 | | componentProps | object | null | component is React.Element, component tag props, not add `style` | 145 | 146 | ### Thumb 147 | 148 | | name | type | default | description | 149 | |----------|-----------------|--------------|-----------------------| 150 | | children | React.Element | - | must | 151 | | prefixCls | string | - | user class | 152 | | component | string / React.Element | `div` | component tag | 153 | | componentProps | object | null | component is React.Element, component tag props, not add `style` | 154 | -------------------------------------------------------------------------------- /assets/index.less: -------------------------------------------------------------------------------- 1 | @banner: banner-anim; 2 | .@{banner} { 3 | position: relative; 4 | overflow: hidden; 5 | min-height: 100px; 6 | &-elem { 7 | height: 100%; 8 | & > * { 9 | position: relative; 10 | } 11 | &-mask { 12 | position: relative; 13 | overflow: hidden; 14 | width: 100%; 15 | } 16 | } 17 | &-arrow { 18 | cursor: pointer; 19 | position: absolute; 20 | z-index: 10; 21 | &-default { 22 | position: absolute; 23 | width: 20px; 24 | height: 60px; 25 | margin-top: -30px; 26 | top: 50%; 27 | background: fade(#000, 30); 28 | &.next { 29 | right: 0; 30 | } 31 | &.next, &.prev { 32 | &:before, &:after { 33 | width: 2px; 34 | height: 15px; 35 | background: #fff; 36 | display: block; 37 | content: ' '; 38 | position: absolute; 39 | 40 | } 41 | } 42 | &.next { 43 | &:before { 44 | transform: rotate(-40deg); 45 | top: 18px; 46 | left: 10px; 47 | } 48 | &:after { 49 | transform: rotate(40deg); 50 | bottom: 17px; 51 | left: 10px; 52 | } 53 | } 54 | &.prev { 55 | &:before { 56 | transform: rotate(40deg); 57 | top: 18px; 58 | left: 8px; 59 | } 60 | &:after { 61 | transform: rotate(-40deg); 62 | bottom: 17px; 63 | left: 8px; 64 | } 65 | } 66 | } 67 | 68 | } 69 | &-thumb { 70 | position: absolute; 71 | bottom: 0; 72 | margin: 0; 73 | padding: 0; 74 | width: 100%; 75 | text-align: center; 76 | pointer-events: none; 77 | z-index: 10; 78 | & > span { 79 | pointer-events: auto; 80 | cursor: pointer; 81 | display: inline-block; 82 | list-style: none; 83 | } 84 | &-default { 85 | height: 40px; 86 | line-height: 40px; 87 | & span { 88 | width: 8px; 89 | height: 8px; 90 | border-radius: 8px; 91 | margin: 0 5px; 92 | background: fade(#666, 35); 93 | transition: background .3s; 94 | box-shadow: 0 0 3px rgba(0, 0, 0, .25); 95 | &:active, &.active { 96 | background: #fff; 97 | } 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['airbnb', 'prettier', 'prettier/react'], 3 | parser: 'babel-eslint', 4 | rules: { 5 | strict: 0, 6 | 'no-param-reassign': 0, 7 | 'arrow-body-style': 0, 8 | 'id-length': 0, 9 | 'import/no-extraneous-dependencies': 0, 10 | 'import/no-unresolved': 0, 11 | 'import/extensions': 0, 12 | 'no-underscore-dangle': 0, 13 | 'react/jsx-filename-extension': 0, 14 | 'react/require-default-props': 0, 15 | 'react/forbid-prop-types': 0, 16 | 'react/no-unused-prop-types': 0, 17 | 'no-plusplus': 0, 18 | 'no-bitwise': [2, { allow: ['~'] }], 19 | 'jsx-a11y/no-static-element-interactions': 0, 20 | 'jsx-a11y/anchor-has-content': 0, 21 | 'jsx-a11y/click-events-have-key-events': 0, 22 | 'jsx-a11y/anchor-is-valid': 0, 23 | 'jsx-a11y/label-has-for': 0, 24 | 'prefer-destructuring': 0, 25 | 'no-class-assign': 0, 26 | 'react/no-array-index-key': 0, 27 | 'react/no-find-dom-node': 0, 28 | }, 29 | globals: { 30 | expect: true, 31 | document: true, 32 | window: true, 33 | }, 34 | env: { 35 | jest: true, 36 | node: true, 37 | mocha: true, 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /examples/assets/arrow.less: -------------------------------------------------------------------------------- 1 | .user-arrow { 2 | top: 50%; 3 | margin-top: -40px; 4 | & .img-wrapper { 5 | width: 120px; 6 | height: 80px; 7 | float: left; 8 | position: relative; 9 | & li { 10 | width: 100%; 11 | height: 100%; 12 | background-size: cover; 13 | background-position: center; 14 | position: absolute; 15 | } 16 | } 17 | & .arrow { 18 | width: 20px; 19 | height: 80px; 20 | background: fade(#000, 30); 21 | position: relative; 22 | &:before, &:after { 23 | width: 2px; 24 | height: 15px; 25 | background: #fff; 26 | display: block; 27 | content: ' '; 28 | position: absolute; 29 | } 30 | } 31 | &.next { 32 | right: -120px; 33 | & .arrow { 34 | float: left; 35 | &:before { 36 | transform: rotate(-40deg); 37 | top: 28px; 38 | left: 10px; 39 | } 40 | &:after { 41 | transform: rotate(40deg); 42 | bottom: 27px; 43 | left: 10px; 44 | } 45 | } 46 | } 47 | &.prev { 48 | left: -120px; 49 | & .arrow { 50 | float: right; 51 | &:before { 52 | transform: rotate(40deg); 53 | top: 28px; 54 | left: 8px; 55 | } 56 | &:after { 57 | transform: rotate(-40deg); 58 | bottom: 27px; 59 | left: 8px; 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /examples/assets/bgParallax.less: -------------------------------------------------------------------------------- 1 | .banner-anim-elem { 2 | .bg { 3 | height: calc(~"100% + 200px"); 4 | top: -100px; 5 | transform: translateY(-100px); 6 | background-size: cover; 7 | background-position: center; 8 | } 9 | } -------------------------------------------------------------------------------- /examples/assets/index.less: -------------------------------------------------------------------------------- 1 | @banner: banner-user; 2 | .@{banner}, 3 | .banner-anim { 4 | height: 360px; 5 | } 6 | 7 | .banner-anim-elem.@{banner}-elem { 8 | color: #fff; 9 | } 10 | 11 | ul, 12 | li { 13 | list-style: none; 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | .banner-thumb-bottom { 19 | .banner-anim-elem { 20 | height: 560px; 21 | } 22 | .banner-anim-thumb { 23 | background: #999; 24 | } 25 | } 26 | 27 | .banner-anim-elem .bg { 28 | width: 100%; 29 | height: 100%; 30 | position: absolute; 31 | top: 0; 32 | left: 0; 33 | overflow: hidden; 34 | } 35 | 36 | .text-wrapper { 37 | color: #fff; 38 | text-align: center; 39 | margin-top: 100px; 40 | h1, 41 | h2 { 42 | color: #fff; 43 | font-weight: lighter; 44 | } 45 | h1 { 46 | font-size: 42px; 47 | margin-bottom: 8px; 48 | line-height: 42px; 49 | font-family: "PingFang SC,Helvetica Neue,Helvetica,Hiragino Sans GB,Microsoft YaHei,\\5FAE\8F6F\96C5\9ED1,Arial,sans-serif"; 50 | } 51 | h2 { 52 | font-size: 12px; 53 | margin-bottom: 12px; 54 | } 55 | p { 56 | line-height: 24px; 57 | margin-bottom: 16px; 58 | } 59 | } 60 | 61 | .icon-wrapper { 62 | position: absolute !important; 63 | width: 100%; 64 | height: 100%; 65 | top: 0; 66 | left: 0; 67 | > div { 68 | position: absolute; 69 | &:nth-child(1) { 70 | left: 10%; 71 | top: 20%; 72 | } 73 | &:nth-child(2) { 74 | left: 40%; 75 | top: 70%; 76 | } 77 | &:nth-child(3) { 78 | right: -20px; 79 | bottom: 80px; 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /examples/assets/thumb.less: -------------------------------------------------------------------------------- 1 | .user-thumb { 2 | overflow: hidden; 3 | background: fade(#fff, 15); 4 | height: 70px; 5 | & > span { 6 | width: 80px; 7 | height: 60px; 8 | margin: 5px 5px; 9 | box-shadow: 0 0 5px fade(#000, 15); 10 | transition: background .3s; 11 | background: transparent; 12 | &.active { 13 | background: fade(#fff, 45); 14 | } 15 | & i { 16 | display: block; 17 | width: 70px; 18 | height: 50px; 19 | margin: 5px; 20 | background-size: cover; 21 | background-position: center; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /examples/autoPlayEffect.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/autoPlayEffect.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import '../assets/index.less'; 10 | 11 | const { Element } = BannerAnim; 12 | const BgElement = Element.BgElement; 13 | function Demo() { 14 | return ( 15 | 20 | 23 | 32 | 33 |

Ant Motion Demo

34 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

35 |
36 | 37 | Ant Motion Demo.Ant Motion Demo 38 | 39 |
40 | 43 | 52 | 53 |

Ant Motion Demo

54 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

55 |
56 | 57 | Ant Motion Demo.Ant Motion Demo 58 | 59 |
60 |
61 | ); 62 | } 63 | 64 | ReactDOM.render(, document.getElementById('__react-content')); 65 | -------------------------------------------------------------------------------- /examples/autoplay.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/autoplay.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import '../assets/index.less'; 10 | 11 | const { Element } = BannerAnim; 12 | const BgElement = Element.BgElement; 13 | function Demo() { 14 | return ( 15 | 16 | 19 | 28 | 29 |

Ant Motion Demo

30 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

31 |
32 | 33 | Ant Motion Demo.Ant Motion Demo 34 | 35 |
36 | 39 | 48 | 49 |

Ant Motion Demo

50 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

51 |
52 | 53 | Ant Motion Demo.Ant Motion Demo 54 | 55 |
56 |
57 | ); 58 | } 59 | 60 | ReactDOM.render(, document.getElementById('__react-content')); 61 | -------------------------------------------------------------------------------- /examples/bgParallax.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/bgParallax.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import '../assets/index.less'; 10 | import './assets/bgParallax.less'; 11 | 12 | const { Element } = BannerAnim; 13 | const BgElement = Element.BgElement; 14 | function Demo() { 15 | return ( 16 | 17 | 20 | 28 | 29 |

Ant Motion Demo

30 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

31 |
32 | 33 | Ant Motion Demo.Ant Motion Demo 34 | 35 |
36 | 39 | 47 | 48 |

Ant Motion Demo

49 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

50 |
51 | 52 | Ant Motion Demo.Ant Motion Demo 53 | 54 |
55 |
56 | ); 57 | } 58 | 59 | ReactDOM.render(, document.getElementById('__react-content')); 60 | -------------------------------------------------------------------------------- /examples/change.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/change.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import '../assets/index.less'; 10 | 11 | const { Element } = BannerAnim; 12 | const BgElement = Element.BgElement; 13 | class Demo extends React.Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | children: [ 20 | 29 | 30 |

Ant Motion Demo

31 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

32 |
33 | 34 | Ant Motion Demo.Ant Motion Demo 35 | 36 |
, 37 | 40 | 49 | 50 |

Ant Motion Demo

51 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

52 |
53 | 54 | Ant Motion Demo.Ant Motion Demo 55 | 56 |
], 57 | }; 58 | } 59 | 60 | componentDidMount() { 61 | const children = this.state.children; 62 | 63 | setTimeout(() => { 64 | children.push( 65 | 68 | 76 | 77 |

Ant Motion Demo

78 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

79 |
80 | 81 | Ant Motion Demo.Ant Motion Demo 82 | 83 |
84 | ); 85 | this.setState({ 86 | children, 87 | }); 88 | }, 2000); 89 | } 90 | 91 | render() { 92 | return ( 93 | 94 | {this.state.children} 95 | 96 | ); 97 | } 98 | } 99 | ReactDOM.render(, document.getElementById('__react-content')); 100 | -------------------------------------------------------------------------------- /examples/customAnimType.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/customAnimType.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import '../assets/index.less'; 10 | 11 | const { animType, setAnimCompToTagComp } = BannerAnim; 12 | 13 | animType.custom = (elem, type, direction, animData) => { 14 | console.log(`custom animType, type:${type}`); // eslint-disable-line no-console 15 | let _y; 16 | const props = { ...elem.props }; 17 | let children = props.children; 18 | if (type === 'enter') { 19 | _y = direction === 'next' ? '100%' : '-100%'; 20 | } else { 21 | _y = direction === 'next' ? '-10%' : '10%'; 22 | children = React.Children.toArray(children).map(setAnimCompToTagComp); 23 | } 24 | return React.cloneElement(elem, { 25 | ...props, 26 | animation: { 27 | ...animData, 28 | y: _y, 29 | type: type === 'enter' ? 'from' : 'to', 30 | }, 31 | }, children); 32 | }; 33 | 34 | const { Element } = BannerAnim; 35 | const BgElement = Element.BgElement; 36 | function Demo() { 37 | return ( 38 | 39 | 42 | 51 | 52 |

Ant Motion Demo

53 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

54 |
55 | 56 | Ant Motion Demo.Ant Motion Demo 57 | 58 |
59 | 62 | 71 | 72 |

Ant Motion Demo

73 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

74 |
75 | 76 | Ant Motion Demo.Ant Motion Demo 77 | 78 |
79 |
80 | ); 81 | } 82 | 83 | ReactDOM.render(, document.getElementById('__react-content')); 84 | -------------------------------------------------------------------------------- /examples/customArrow.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/customArrow.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne, { TweenOneGroup } from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import './assets/arrow.less'; 10 | import '../assets/index.less'; 11 | 12 | const { Element, Arrow } = BannerAnim; 13 | const BgElement = Element.BgElement; 14 | class Demo extends React.Component { 15 | constructor(props) { 16 | super(props); 17 | this.imgArray = [ 18 | 'https://os.alipayobjects.com/rmsportal/IhCNTqPpLeTNnwr.jpg', 19 | 'https://os.alipayobjects.com/rmsportal/uaQVvDrCwryVlbb.jpg', 20 | ]; 21 | this.state = { 22 | intShow: 0, 23 | prevEnter: false, 24 | nextEnter: false, 25 | }; 26 | } 27 | 28 | onChange = (type, int) => { 29 | if (type === 'before') { 30 | this.setState({ 31 | intShow: int, 32 | }); 33 | } 34 | } 35 | 36 | getNextPrevNumber = () => { 37 | let nextInt = this.state.intShow + 1; 38 | let prevInt = this.state.intShow - 1; 39 | if (nextInt >= this.imgArray.length) { 40 | nextInt = 0; 41 | } 42 | if (prevInt < 0) { 43 | prevInt = this.imgArray.length - 1; 44 | } 45 | 46 | return [prevInt, nextInt]; 47 | } 48 | 49 | prevEnter = () => { 50 | this.setState({ 51 | prevEnter: true, 52 | }); 53 | } 54 | 55 | prevLeave = () => { 56 | this.setState({ 57 | prevEnter: false, 58 | }); 59 | } 60 | 61 | nextEnter = () => { 62 | this.setState({ 63 | nextEnter: true, 64 | }); 65 | } 66 | 67 | nextLeave = () => { 68 | this.setState({ 69 | nextEnter: false, 70 | }); 71 | } 72 | 73 | render() { 74 | const intArray = this.getNextPrevNumber(); 75 | return ( 76 | 77 | 80 | 89 | 90 |

Ant Motion Demo

91 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

92 |
93 | 98 | Ant Motion Demo.Ant Motion Demo 99 | 100 |
101 | 104 | 113 | 114 |

Ant Motion Demo

115 |

Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

116 |
117 | 122 | Ant Motion Demo.Ant Motion Demo 123 | 124 |
125 | 130 |
131 | 134 |
  • 138 | 139 | 140 | 145 |
    146 | 149 |
  • 153 | 154 | 155 | 156 | ); 157 | } 158 | } 159 | ReactDOM.render(, document.getElementById('__react-content')); 160 | -------------------------------------------------------------------------------- /examples/customThumb.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/customThumb.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import './assets/thumb.less'; 10 | import '../assets/index.less'; 11 | 12 | const { Element, Thumb } = BannerAnim; 13 | const BgElement = Element.BgElement; 14 | class Demo extends React.Component { 15 | constructor(props) { 16 | super(props); 17 | this.imgArray = [ 18 | 'https://os.alipayobjects.com/rmsportal/IhCNTqPpLeTNnwr.jpg', 19 | 'https://os.alipayobjects.com/rmsportal/uaQVvDrCwryVlbb.jpg', 20 | ]; 21 | this.state = { 22 | enter: false, 23 | }; 24 | } 25 | 26 | onMouseEnter = () => { 27 | this.setState({ 28 | enter: true, 29 | }); 30 | } 31 | 32 | onMouseLeave = () => { 33 | this.setState({ 34 | enter: false, 35 | }); 36 | } 37 | 38 | render() { 39 | const thumbChildren = this.imgArray.map((img, i) => 40 | 41 | ); 42 | return ( 43 | 44 | 47 | 56 | 57 |

    Ant Motion Demo

    58 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    59 |
    60 | 65 | Ant Motion Demo.Ant Motion Demo 66 | 67 |
    68 | 71 | 80 | 81 |

    Ant Motion Demo

    82 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    83 |
    84 | 89 | Ant Motion Demo.Ant Motion Demo 90 | 91 |
    92 | 95 | {thumbChildren} 96 | 97 |
    98 | ); 99 | } 100 | } 101 | ReactDOM.render(, document.getElementById('__react-content')); 102 | -------------------------------------------------------------------------------- /examples/followMouse.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/followMouse.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import '../assets/index.less'; 10 | 11 | const { Element } = BannerAnim; 12 | const BgElement = Element.BgElement; 13 | function Demo() { 14 | return ( 15 | 16 | 28 | 38 | 39 |

    Ant Motion Demo

    40 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    41 |
    42 | 43 | Ant Motion Demo.Ant MotionDemo 44 | 45 |
    46 | 49 | 58 | 59 |

    Ant Motion Demo

    60 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    61 |
    62 | 63 | Ant Motion Demo.Ant Motion Demo 64 | 65 |
    66 |
    67 | ); 68 | } 69 | 70 | ReactDOM.render(, document.getElementById('__react-content')); 71 | -------------------------------------------------------------------------------- /examples/leaveChildHide.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/leaveChildHide.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import '../assets/index.less'; 10 | 11 | const { Element } = BannerAnim; 12 | const BgElement = Element.BgElement; 13 | class Demo extends React.Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | delay: 0, 18 | }; 19 | this.openSlide = false; 20 | } 21 | 22 | onChange = (e, int) => { 23 | // 在切换到下一个后把延时改掉。 24 | if (int === 1 && e === 'after' && !this.openSlide) { 25 | this.setState({ 26 | delay: 600, 27 | }); 28 | this.openSlide = true; 29 | } 30 | } 31 | 32 | onClick = () => { 33 | this.banner.slickGoTo(1); 34 | } 35 | 36 | render() { 37 | return ( 38 |
    39 | 点击跳到第二块 40 | { this.banner = c; }} 46 | > 47 | 51 | 60 | 61 |

    Ant Motion Demo

    62 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    63 |
    64 | 67 | Ant Motion Demo.Ant Motion Demo 68 | 69 |
    70 | 74 | 83 | 84 |

    Ant Motion Demo

    85 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    86 |
    87 | 88 | Ant Motion Demo.Ant Motion Demo 89 | 90 |
    91 |
    92 |
    93 | ); 94 | } 95 | } 96 | ReactDOM.render(, document.getElementById('__react-content')); 97 | -------------------------------------------------------------------------------- /examples/parallax.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/parallax.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import { Button } from 'antd'; 9 | import 'antd/dist/antd.less'; 10 | import './assets/index.less'; 11 | import '../assets/index.less'; 12 | 13 | 14 | const { Element } = BannerAnim; 15 | const BgElement = Element.BgElement; 16 | 17 | const svgNode = ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ) 38 | function Demo() { 39 | return ( 40 | 41 | 58 | 70 | 71 |
    72 | {svgNode} 73 |
    74 |
    75 | {svgNode} 76 |
    77 |
    78 | {svgNode} 79 |
    80 |
    81 | 82 |

    Motion Design

    83 |

    Animation specification and components of Ant Design.

    84 |

    使用 Ant Motion 能够快速在 React 框架中使用动画

    85 |

    我们提供了单项,组合动画,以及整套解决方案

    86 |
    87 |
    88 |
    89 | 92 | 101 | 102 |

    Ant Motion Demo

    103 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    104 |
    105 | 106 | Ant Motion Demo.Ant Motion Demo 107 | 108 |
    109 |
    110 | ); 111 | } 112 | 113 | ReactDOM.render(, document.getElementById('__react-content')); 114 | -------------------------------------------------------------------------------- /examples/simple.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/simple.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import '../assets/index.less'; 10 | 11 | const { Element } = BannerAnim; 12 | const BgElement = Element.BgElement; 13 | function Demo() { 14 | return ( 15 | 16 | 20 | 29 | 30 |

    Ant Motion Demo

    31 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    32 |
    33 | 34 | Ant Motion Demo.Ant Motion Demo 35 | 36 |
    37 | 41 | 50 | 51 |

    Ant Motion Demo

    52 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    53 |
    54 | 55 | Ant Motion Demo.Ant Motion Demo 56 | 57 |
    58 |
    59 | ); 60 | } 61 | 62 | ReactDOM.render(, document.getElementById('__react-content')); 63 | -------------------------------------------------------------------------------- /examples/thumbBottom.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/thumbBottom.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import '../assets/index.less'; 10 | 11 | const { Element } = BannerAnim; 12 | const BgElement = Element.BgElement; 13 | function Demo() { 14 | return ( 15 | 16 | 19 | 28 | 29 |

    Ant Motion Demo

    30 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    31 |
    32 | 33 | Ant Motion Demo.Ant Motion Demo 34 | 35 |
    36 | 39 | 48 | 49 |

    Ant Motion Demo

    50 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    51 |
    52 | 53 | Ant Motion Demo.Ant Motion Demo 54 | 55 |
    56 |
    57 | ); 58 | } 59 | 60 | ReactDOM.render(, document.getElementById('__react-content')); 61 | -------------------------------------------------------------------------------- /examples/videoBg.html: -------------------------------------------------------------------------------- 1 | placeholder -------------------------------------------------------------------------------- /examples/videoBg.js: -------------------------------------------------------------------------------- 1 | // use jsx to render html, do not modify simple.html 2 | /* eslint jsx-a11y/media-has-caption: 0 */ 3 | import BannerAnim from 'rc-banner-anim'; 4 | import QueueAnim from 'rc-queue-anim'; 5 | import TweenOne from 'rc-tween-one'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import './assets/index.less'; 9 | import '../assets/index.less'; 10 | 11 | const { Element } = BannerAnim; 12 | const BgElement = Element.BgElement; 13 | function Demo() { 14 | return ( 15 | 16 | 19 | 20 | 23 | 24 | 25 |

    Ant Motion Demo

    26 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    27 |
    28 | 29 | Ant Motion Demo.Ant MotionDemo 30 | 31 |
    32 | 35 | 44 | 45 |

    Ant Motion Demo

    46 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    47 |
    48 | 49 | Ant Motion Demo.Ant MotionDemo 50 | 51 |
    52 | 55 | 64 | 65 |

    Ant Motion Demo

    66 |

    Ant Motion Demo.Ant Motion Demo.Ant Motion Demo.Ant Motion Demo

    67 |
    68 | 69 | Ant Motion Demo.Ant Motion Demo 70 | 71 |
    72 |
    73 | ); 74 | } 75 | 76 | ReactDOM.render(, document.getElementById('__react-content')); 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rc-banner-anim", 3 | "version": "2.4.5", 4 | "description": "banner-anim animation component for react", 5 | "keywords": [ 6 | "react", 7 | "react-component", 8 | "react-banner-anim", 9 | "banner-anim" 10 | ], 11 | "homepage": "https://github.com/react-component/banner-anim", 12 | "author": "", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/react-component/banner-anim.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/react-component/banner-anim/issues" 19 | }, 20 | "files": [ 21 | "lib", 22 | "assets/*.css", 23 | "dist", 24 | "es", 25 | "typings" 26 | ], 27 | "licenses": "MIT", 28 | "main": "./lib/index", 29 | "module": "./es/index", 30 | "config": { 31 | "port": 8012, 32 | "entry": { 33 | "rc-banner-anim": [ 34 | "./assets/index.less", 35 | "./src/index.js" 36 | ] 37 | } 38 | }, 39 | "scripts": { 40 | "dist": "rc-tools run dist", 41 | "build": "rc-tools run build", 42 | "gh-pages": "rc-tools run gh-pages", 43 | "start": "rc-tools run server", 44 | "compile": "rc-tools run compile --babel-runtime", 45 | "pub": "rc-tools run pub --babel-runtime", 46 | "lint": "rc-tools run lint --fix", 47 | "karma": "rc-test run karma", 48 | "saucelabs": "rc-test run saucelabs", 49 | "test": "rc-test run test", 50 | "prettier": "rc-tools run prettier", 51 | "chrome-test": "rc-test run chrome-test", 52 | "coverage": "rc-test run coverage", 53 | "validate": "npm ls" 54 | }, 55 | "devDependencies": { 56 | "@types/react": "^16.0.0", 57 | "antd": "^3.6.0", 58 | "core-js": "^2.5.1", 59 | "expect.js": "0.3.x", 60 | "precommit-hook": "^3.0.0", 61 | "rc-queue-anim": "^1.6.0", 62 | "rc-test": "6.x", 63 | "rc-tools": "8.x", 64 | "react": "^16.4.0", 65 | "react-dom": "^16.4.0", 66 | "tslint-config-prettier": "^1.17.0", 67 | "tslint-react": "^3.6.0", 68 | "typescript": "3.x" 69 | }, 70 | "dependencies": { 71 | "babel-runtime": "6.x", 72 | "prop-types": "^15.5.0", 73 | "rc-tween-one": "^2.4.0", 74 | "react-lifecycles-compat": "^3.0.4", 75 | "style-utils": "~0.2.0", 76 | "tween-functions": "1.x" 77 | }, 78 | "pre-commit": [ 79 | "lint", 80 | "test" 81 | ], 82 | "types": "typings/index.d.ts" 83 | } 84 | -------------------------------------------------------------------------------- /src/Arrow.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class Arrow extends Component { 5 | onClick = (e) => { 6 | if (e.stopPropagation) { 7 | e.stopPropagation(); 8 | } 9 | this.props[this.props.arrowType](e); 10 | } 11 | 12 | render() { 13 | const { 14 | arrowType, 15 | next, 16 | prev, 17 | component, 18 | componentProps, 19 | defaultBool, 20 | prefixCls, 21 | children, 22 | ...props 23 | } = this.props; 24 | let { className } = this.props; 25 | const defaultClass = `${className}-default`; 26 | className = `${className} ${prefixCls || ''}`.trim(); 27 | className = !defaultBool ? className : `${className} ${defaultClass}`.trim(); 28 | className = `${className} ${arrowType}`; 29 | const $props = { 30 | ...props, 31 | ...componentProps, 32 | className, 33 | onClick: this.onClick, 34 | }; 35 | return React.createElement(component, $props, children); 36 | } 37 | } 38 | 39 | Arrow.propTypes = { 40 | children: PropTypes.any, 41 | style: PropTypes.object, 42 | className: PropTypes.string, 43 | prefixCls: PropTypes.string, 44 | component: PropTypes.any, 45 | arrowType: PropTypes.string, 46 | defaultBool: PropTypes.bool, 47 | next: PropTypes.func, 48 | prev: PropTypes.func, 49 | componentProps: PropTypes.object, 50 | }; 51 | Arrow.defaultProps = { 52 | component: 'div', 53 | className: 'banner-anim-arrow', 54 | componentProps: {}, 55 | }; 56 | 57 | Arrow.isBannerAnimArrow = true; 58 | 59 | export default Arrow; 60 | -------------------------------------------------------------------------------- /src/BannerAnim.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import PropTypes from 'prop-types'; 4 | import { ticker } from 'rc-tween-one'; 5 | import Arrow from './Arrow'; 6 | import Thumb from './Thumb'; 7 | import { toArrayChildren, dataToArray } from './utils'; 8 | import animType from './anim'; 9 | 10 | class BannerAnim extends Component { 11 | 12 | static getDerivedStateFromProps(props, { prevProps, $self }) { 13 | const nextState = { 14 | prevProps: props, 15 | }; 16 | if (prevProps && props !== prevProps) { 17 | $self.tweenBool = false; 18 | } 19 | return nextState;// eslint-disable-line 20 | } 21 | 22 | 23 | constructor(props) { 24 | super(props); 25 | this.state = { 26 | currentShow: this.props.initShow, 27 | direction: null, 28 | domRect: {}, 29 | $self: this,// eslint-disable-line 30 | }; 31 | this.tweenBool = false; 32 | } 33 | 34 | componentDidMount() { 35 | this.getDomDataSetToState(); 36 | if (window.addEventListener) { 37 | window.addEventListener('resize', this.getDomDataSetToState); 38 | window.addEventListener('touchend', this.onTouchEnd); 39 | window.addEventListener('mouseup', this.onTouchEnd); 40 | } else { 41 | window.attachEvent('onresize', this.getDomDataSetToState); 42 | window.attachEvent('ontouchend', this.onTouchEnd); 43 | window.attachEvent('onmouseup', this.onTouchEnd); 44 | } 45 | if (this.props.autoPlay) { 46 | this.autoPlay(); 47 | } 48 | } 49 | 50 | componentWillUnmount() { 51 | if (this.autoPlayId) { 52 | ticker.clear(this.autoPlayId); 53 | this.autoPlayId = 0; 54 | } 55 | if (window.addEventListener) { 56 | window.removeEventListener('touchend', this.onTouchEnd); 57 | window.removeEventListener('mouseup', this.onTouchEnd); 58 | window.removeEventListener('resize', this.getDomDataSetToState); 59 | } else { 60 | window.detachEvent('ontouchend', this.onTouchEnd); 61 | window.attachEvent('onmouseup', this.onTouchEnd); 62 | window.detachEvent('onresize', this.getDomDataSetToState); 63 | } 64 | } 65 | 66 | onMouseEnter = (e) => { 67 | this.props.onMouseEnter(e); 68 | if (this.props.autoPlay && this.props.autoPlayEffect) { 69 | ticker.clear(this.autoPlayId); 70 | this.autoPlayId = -1; 71 | } 72 | } 73 | 74 | onMouseLeave = (e) => { 75 | this.props.onMouseLeave(e); 76 | if (this.props.autoPlay && this.props.autoPlayEffect) { 77 | this.autoPlay(); 78 | } 79 | } 80 | 81 | onTouchStart = (e) => { 82 | if (e.touches && e.touches.length > 1 83 | || this.elemWrapper.length <= 1 84 | || this.getDomIsArrowOrThumb(e) 85 | || e.button === 2 || this.tweenBool) { 86 | return; 87 | } 88 | if (this.props.autoPlay) { 89 | ticker.clear(this.autoPlayId); 90 | this.autoPlayId = -1; 91 | } 92 | this.animType = this.getAnimType(this.props.type); 93 | this.currentShow = this.state.currentShow; 94 | // this.mouseMoveType = 'start'; 95 | this.mouseStartXY = { 96 | startX: e.touches === undefined ? e.clientX : e.touches[0].clientX, 97 | startY: e.touches === undefined ? e.clientY : e.touches[0].clientY, 98 | }; 99 | } 100 | 101 | onTouchMove = (e) => { 102 | if (!this.mouseStartXY || e.touches && e.touches.length > 1 || this.tweenBool) { 103 | return; 104 | } 105 | const { differ, rectName } = this.getDiffer(e, e.touches); 106 | if (!differ) { 107 | return; 108 | } 109 | const ratio = differ / this.state.domRect[rectName]; 110 | const ratioType = ratio < 0 ? '+' : '-'; 111 | let currentShow = this.currentShow; 112 | this.mouseMoveType = 'update'; 113 | if (this.ratioType !== ratioType) { 114 | this.ratioType = ratioType; 115 | this.mouseMoveType = 'reChild'; 116 | this.setState({ 117 | currentShow, 118 | }); 119 | return; 120 | } 121 | if ((this.animType === animType.gridBar || this.animType === animType.grid) && e.touches) { 122 | return; 123 | } 124 | this.ratio = ratio; 125 | if (this.ratio) { 126 | let type; 127 | if (this.ratio < 0) { 128 | currentShow += 1; 129 | type = 'next'; 130 | } else { 131 | currentShow -= 1; 132 | type = 'prev'; 133 | } 134 | this.ratio = Math.abs(this.ratio); 135 | this.ratio = this.ratio > 0.99 ? 0.99 : this.ratio; 136 | currentShow = currentShow >= this.elemWrapper.length ? 0 : currentShow; 137 | currentShow = currentShow < 0 ? this.elemWrapper.length - 1 : currentShow; 138 | this.setState({ 139 | currentShow, 140 | direction: type, 141 | }); 142 | } 143 | } 144 | 145 | onTouchEnd = (e) => { 146 | if (!this.mouseStartXY || 147 | e.changedTouches && e.changedTouches.length > 1 || 148 | this.tweenBool 149 | ) { 150 | return; 151 | } 152 | const { differ, rectName } = this.getDiffer(e, e.changedTouches); 153 | this.mouseStartXY = null; 154 | if (this.props.autoPlay && this.autoPlayId === -1) { 155 | this.autoPlay(); 156 | } 157 | this.mouseMoveType = 'end'; 158 | if (!differ) { 159 | this.mouseMoveType = ''; 160 | return 161 | } 162 | if ((this.animType === animType.gridBar || this.animType === animType.grid) && e.changedTouches) { 163 | let currentShow = this.currentShow; 164 | const ratio = differ / this.state.domRect[rectName] * 2; 165 | if (ratio < 0) { 166 | currentShow += 1; 167 | } else { 168 | currentShow -= 1; 169 | } 170 | currentShow = currentShow >= this.elemWrapper.length ? 0 : currentShow; 171 | currentShow = currentShow < 0 ? this.elemWrapper.length - 1 : currentShow; 172 | this.ratio = 0; 173 | this.mouseMoveType = ''; 174 | this.slickGoTo(currentShow, true); 175 | this.tweenBool = true; 176 | return; 177 | } 178 | 179 | if (this.ratio > 0.3) { 180 | this.forceUpdate(() => { 181 | this.ratio = 0; 182 | this.mouseMoveType = ''; 183 | }); 184 | } else { 185 | this.setState({ 186 | currentShow: this.currentShow, 187 | direction: this.ratioType === '+' ? 'prev' : 'next' 188 | }, () => { 189 | this.ratio = 0; 190 | this.mouseMoveType = ''; 191 | }); 192 | } 193 | } 194 | 195 | getDiffer = (e, touches) => { 196 | const currentX = touches === undefined ? e.clientX : touches[0].clientX; 197 | const currentY = touches === undefined ? e.clientY : touches[0].clientY; 198 | const differX = currentX - this.mouseStartXY.startX; 199 | const differY = currentY - this.mouseStartXY.startY; 200 | let differ = Math.max(Math.abs(differX), Math.abs(differY)); 201 | differ = differ === Math.abs(differX) ? differX : differY; 202 | return { 203 | differ, 204 | rectName: differ === differX ? 'width' : 'height', 205 | }; 206 | } 207 | 208 | getDomIsArrowOrThumb = (e) => { 209 | const arrowClassName = e.target.className; 210 | const thumbClassName = e.target.parentNode.className; 211 | if ( 212 | arrowClassName.indexOf('banner-anim-arrow') >= 0 || 213 | thumbClassName.indexOf('banner-anim-thumb') >= 0 214 | ) { 215 | return true; 216 | } 217 | return false; 218 | } 219 | 220 | getRenderChildren = (children) => { 221 | const elem = []; 222 | const arrow = []; 223 | let thumb; 224 | let elementKeyNum = 0; 225 | let thumbKeyNum = 0; 226 | 227 | toArrayChildren(children).forEach((item, i) => { 228 | if (!item) { 229 | return; 230 | } 231 | const itemProps = { ...item.props }; 232 | if (item.type.isBannerAnimElement) { 233 | itemProps.key = item.key || `element-${elementKeyNum}`; 234 | elementKeyNum += 1; 235 | itemProps.callBack = this.animEnd; 236 | itemProps.show = this.state.currentShow === i; 237 | itemProps.animType = this.animType; 238 | itemProps.duration = this.props.duration; 239 | itemProps.delay = this.props.delay; 240 | itemProps.ease = this.props.ease; 241 | itemProps.sync = this.props.sync || itemProps.sync; 242 | itemProps.elemOffset = { 243 | top: this.state.domRect.top, 244 | width: this.state.domRect.width, 245 | height: this.state.domRect.height, 246 | }; 247 | itemProps.direction = this.state.direction; 248 | itemProps.ratio = this.ratio; 249 | itemProps.mouseMoveType = this.mouseMoveType; 250 | elem.push(React.cloneElement(item, itemProps)); 251 | } else if (item.type.isBannerAnimArrow) { 252 | itemProps.next = this.next; 253 | itemProps.prev = this.prev; 254 | itemProps.key = item.key || itemProps.arrowType; 255 | arrow.push(React.cloneElement(item, itemProps)); 256 | } else if (item.type.isBannerAnimThumb) { 257 | itemProps.key = item.key || `thumb-${thumbKeyNum}`; 258 | thumbKeyNum += 1; 259 | itemProps.thumbClick = this.slickGoTo; 260 | itemProps.active = this.state.currentShow; 261 | thumb = React.cloneElement(item, itemProps); 262 | } 263 | }); 264 | if (elem.length > 1) { 265 | if (!arrow.length && this.props.arrow) { 266 | arrow.push( 267 | , 268 | 269 | ); 270 | } 271 | if (!thumb && this.props.thumb) { 272 | thumb = (); 277 | } 278 | } 279 | this.elemWrapper = elem; 280 | return elem.concat(arrow, thumb); 281 | } 282 | 283 | getDomDataSetToState = () => { 284 | this.dom = ReactDOM.findDOMNode(this); 285 | const domRect = this.dom.getBoundingClientRect(); 286 | this.setState({ 287 | domRect, 288 | }); 289 | this.tweenBool = false; 290 | } 291 | 292 | getAnimType = (type) => { 293 | const typeArray = type ? dataToArray(type) : Object.keys(animType); 294 | const random = Math.round(Math.random() * (typeArray.length - 1)); 295 | return animType[typeArray[random]]; 296 | } 297 | 298 | autoPlay = () => { 299 | if (!this.mouseStartXY) { 300 | ticker.clear(this.autoPlayId); 301 | this.autoPlayId = ticker.interval(this.next, this.props.autoPlaySpeed); 302 | } 303 | } 304 | 305 | animTweenStart = (show, type, noGetAnimType) => { 306 | if (!noGetAnimType) { 307 | this.animType = this.getAnimType(this.props.type); 308 | } 309 | this.props.onChange('before', show); 310 | this.setState({ 311 | currentShow: show, 312 | direction: type, 313 | }); 314 | } 315 | 316 | animEnd = (type) => { 317 | if (type === 'enter') { 318 | this.tweenBool = false; 319 | this.props.onChange('after', this.state.currentShow); 320 | } 321 | } 322 | 323 | next = () => { 324 | if (!this.tweenBool) { 325 | this.tweenBool = true; 326 | let newShow = this.state.currentShow; 327 | newShow++; 328 | if (newShow >= this.elemWrapper.length) { 329 | newShow = 0; 330 | } 331 | this.animTweenStart(newShow, 'next'); 332 | } 333 | } 334 | 335 | prev = () => { 336 | if (!this.tweenBool) { 337 | this.tweenBool = true; 338 | let newShow = this.state.currentShow; 339 | newShow--; 340 | if (newShow < 0) { 341 | newShow = this.elemWrapper.length - 1; 342 | } 343 | this.animTweenStart(newShow, 'prev'); 344 | } 345 | } 346 | 347 | slickGoTo = (i, noGetAnimType) => { 348 | if (!this.tweenBool && i !== this.state.currentShow) { 349 | this.tweenBool = true; 350 | const type = i > this.state.currentShow ? 'next' : 'prev'; 351 | this.animTweenStart(i, type, noGetAnimType); 352 | } 353 | } 354 | 355 | render() { 356 | const { 357 | type, 358 | prefixCls, 359 | component, 360 | initShow, 361 | duration, 362 | delay, 363 | ease, 364 | arrow, 365 | thumb, 366 | autoPlaySpeed, 367 | autoPlay, 368 | sync, 369 | dragPlay, 370 | autoPlayEffect, 371 | ...props 372 | } = this.props; 373 | const childrenToRender = this.getRenderChildren(props.children); 374 | props.className = `${props.className} ${prefixCls || ''}`.trim(); 375 | props.style = { ...props.style }; 376 | props.onMouseEnter = this.onMouseEnter; 377 | props.onMouseLeave = this.onMouseLeave; 378 | if (childrenToRender.length > 1 && this.props.dragPlay) { 379 | props.onTouchStart = this.onTouchStart; 380 | props.onMouseDown = this.onTouchStart; 381 | props.onTouchMove = this.onTouchMove; 382 | props.onMouseMove = this.onTouchMove; 383 | // props.onTouchEnd = this.onTouchEnd; 384 | // props.onMouseUp = this.onTouchEnd; 385 | } 386 | return React.createElement(this.props.component, props, childrenToRender); 387 | } 388 | } 389 | BannerAnim.propTypes = { 390 | children: PropTypes.any, 391 | style: PropTypes.object, 392 | className: PropTypes.string, 393 | prefixCls: PropTypes.string, 394 | component: PropTypes.any, 395 | arrow: PropTypes.bool, 396 | thumb: PropTypes.bool, 397 | initShow: PropTypes.number, 398 | type: PropTypes.any, 399 | duration: PropTypes.number, 400 | delay: PropTypes.number, 401 | ease: PropTypes.string, 402 | autoPlay: PropTypes.bool, 403 | autoPlaySpeed: PropTypes.number, 404 | autoPlayEffect: PropTypes.bool, 405 | onChange: PropTypes.func, 406 | onMouseEnter: PropTypes.func, 407 | onMouseLeave: PropTypes.func, 408 | sync: PropTypes.bool, 409 | dragPlay: PropTypes.bool, 410 | }; 411 | BannerAnim.defaultProps = { 412 | component: 'div', 413 | className: 'banner-anim', 414 | initShow: 0, 415 | duration: 450, 416 | delay: 0, 417 | ease: 'easeInOutQuad', 418 | arrow: true, 419 | thumb: true, 420 | autoPlaySpeed: 5000, 421 | autoPlayEffect: true, 422 | dragPlay: true, 423 | onChange: () => { 424 | }, 425 | onMouseEnter: () => { 426 | }, 427 | onMouseLeave: () => { 428 | }, 429 | }; 430 | BannerAnim.isBannerAnim = true; 431 | export default BannerAnim; 432 | -------------------------------------------------------------------------------- /src/BgElement.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import PropTypes from 'prop-types'; 4 | import { Tween } from 'rc-tween-one'; 5 | import { 6 | stylesToCss, 7 | } from 'style-utils'; 8 | import { 9 | currentScrollTop, 10 | toArrayChildren, 11 | windowHeight, 12 | } from './utils'; 13 | import animType from './anim'; 14 | 15 | export default class BgElement extends React.Component { 16 | static getDerivedStateFromProps(props, { prevProps, $self }) { 17 | const nextState = { 18 | prevProps: props, 19 | }; 20 | if (prevProps && props !== prevProps) { 21 | if (props.show) { 22 | // 取 dom 在 render 之后; 23 | setTimeout(() => { 24 | if ($self.video && prevProps.videoResize && $self.videoLoad) { 25 | $self.onResize(); 26 | } 27 | if (prevProps.scrollParallax) { 28 | $self.onScroll(); 29 | } 30 | }) 31 | } 32 | } 33 | return nextState; 34 | } 35 | 36 | constructor(props) { 37 | super(props); 38 | this.isVideo = toArrayChildren(props.children).some(item => item.type === 'video'); 39 | if (this.isVideo) { 40 | // 如果是 video,删除 grid 系列,位置发生变化,重加载了 video; 41 | delete animType.grid; 42 | delete animType.gridBar; 43 | } 44 | if (props.scrollParallax) { 45 | this.scrollParallaxDuration = props.scrollParallax.duration || 450; 46 | } 47 | this.video = null; 48 | this.videoLoad = false; 49 | this.state = { 50 | $self: this, 51 | }; 52 | } 53 | 54 | componentDidMount() { 55 | this.dom = ReactDOM.findDOMNode(this); 56 | if (!this.videoLoad) { 57 | if (this.video && this.props.videoResize) { 58 | this.video.onloadeddata = this.videoLoadedData; 59 | } 60 | } 61 | if (this.props.scrollParallax) { 62 | this.tween = new Tween(this.dom, [{ 63 | ease: 'linear', // 放前面是为了在外面设置了可覆盖。 64 | ...this.props.scrollParallax, 65 | }]); 66 | this.tween.init(); 67 | this.onScroll(); 68 | if (window.addEventListener) { 69 | window.addEventListener('scroll', this.onScroll); 70 | } else { 71 | window.attachEvent('onscroll', this.onScroll); 72 | } 73 | } 74 | } 75 | 76 | componentWillUnmount() { 77 | if (window.addEventListener) { 78 | window.removeEventListener('resize', this.onResize); 79 | window.removeEventListener('scroll', this.onScroll); 80 | } else { 81 | window.detachEvent('onresize', this.onResize); 82 | window.detachEvent('onscroll', this.onScroll); 83 | } 84 | } 85 | 86 | onScroll = () => { 87 | const scrollTop = currentScrollTop(); 88 | const domRect = this.dom.parentNode.getBoundingClientRect(); 89 | const offsetTop = domRect.top + scrollTop; 90 | const height = Math.max(domRect.height, windowHeight()); 91 | const elementShowHeight = scrollTop - offsetTop + height; 92 | let scale = elementShowHeight / (height + domRect.height); 93 | scale = scale || 0; 94 | scale = scale >= 1 ? 1 : scale; 95 | this.tween.frame(scale * this.scrollParallaxDuration); 96 | }; 97 | 98 | onResize = () => { 99 | if (!this.props.show) { 100 | return; 101 | } 102 | const domRect = this.dom.getBoundingClientRect(); 103 | const videoDomRect = this.video.getBoundingClientRect(); 104 | this.videoLoad = true; 105 | let scale; 106 | const videoRect = { 107 | display: 'block', 108 | position: 'relative', 109 | top: 0, 110 | left: 0, 111 | }; 112 | if (domRect.width / domRect.height > videoDomRect.width / videoDomRect.height) { 113 | scale = domRect.width / videoDomRect.width; 114 | videoRect.width = domRect.width; 115 | videoRect.height = videoDomRect.height * scale; 116 | videoRect.top = -(videoRect.height - domRect.height) / 2; 117 | } else { 118 | scale = domRect.height / videoDomRect.height; 119 | videoRect.height = domRect.height; 120 | videoRect.width = videoDomRect.width * scale; 121 | videoRect.left = -(videoRect.width - domRect.width) / 2; 122 | } 123 | 124 | Object.keys(videoRect).forEach(key => { 125 | this.video.style[key] = stylesToCss(key, videoRect[key]); 126 | }); 127 | }; 128 | 129 | videoLoadedData = () => { 130 | this.onResize(); 131 | if (window.addEventListener) { 132 | window.addEventListener('resize', this.onResize); 133 | } else { 134 | window.attachEvent('onresize', this.onResize); 135 | } 136 | }; 137 | 138 | render() { 139 | const { 140 | videoResize, 141 | scrollParallax, 142 | show, 143 | component, 144 | componentProps, 145 | ...props 146 | } = this.props; 147 | if (this.isVideo && videoResize) { 148 | const children = toArrayChildren(props.children).map((item, i) => 149 | React.cloneElement(item, { 150 | ...item.props, key: item.key || `bg-video-${i}`, ref: (c) => { 151 | this.video = c; 152 | if (typeof item.ref === 'function') { 153 | item.ref(c); 154 | } 155 | } 156 | }) 157 | ); 158 | props.children = children.length === 1 ? children[0] : children; 159 | } 160 | return React.createElement(this.props.component, { ...props, ...componentProps }); 161 | } 162 | } 163 | 164 | BgElement.propTypes = { 165 | className: PropTypes.string, 166 | style: PropTypes.object, 167 | children: PropTypes.any, 168 | component: PropTypes.any, 169 | videoResize: PropTypes.bool, 170 | scrollParallax: PropTypes.object, 171 | show: PropTypes.bool, 172 | componentProps: PropTypes.object, 173 | }; 174 | 175 | BgElement.defaultProps = { 176 | component: 'div', 177 | videoResize: true, 178 | componentProps: {}, 179 | }; 180 | 181 | BgElement.isBannerAnimBgElement = true; 182 | -------------------------------------------------------------------------------- /src/Element.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import ReactDOM from 'react-dom'; 4 | import { polyfill } from 'react-lifecycles-compat'; 5 | import TweenOne, { ticker } from 'rc-tween-one'; 6 | import easeTween from 'tween-functions'; 7 | import { 8 | getGsapType, 9 | isConvert, 10 | stylesToCss, 11 | checkStyleName, 12 | } from 'style-utils'; 13 | 14 | import BgElement from './BgElement'; 15 | import { 16 | currentScrollTop, 17 | currentScrollLeft, 18 | dataToArray, 19 | toArrayChildren, 20 | setAnimCompToTagComp, 21 | } from './utils'; 22 | import animType from './anim'; 23 | 24 | function noop() { 25 | } 26 | 27 | class Element extends Component { 28 | static getDerivedStateFromProps(props, { prevProps, $self, show, mouseMoveType }) { 29 | const nextState = { 30 | prevProps: props, 31 | }; 32 | if (prevProps && props !== prevProps) { 33 | if ($self.tickerId !== -1) { 34 | ticker.clear($self.tickerId); 35 | $self.tickerId = -1; 36 | } 37 | const followParallax = props.followParallax; 38 | if ($self.followParallax && !followParallax) { 39 | $self.reFollowParallax(); 40 | } else { 41 | $self.followParallax = followParallax; 42 | } 43 | if (show || props.show || mouseMoveType) { 44 | nextState.mouseMoveType = props.mouseMoveType; 45 | } 46 | } 47 | return nextState; 48 | } 49 | 50 | constructor(props) { 51 | super(props); 52 | this.state = { 53 | show: props.show, 54 | $self: this, 55 | }; 56 | this.tickerId = -1; 57 | this.enterMouse = null; 58 | this.delayTimeout = null; 59 | this.followParallax = props.followParallax; 60 | this.transform = checkStyleName('transform'); 61 | } 62 | 63 | componentDidMount() { 64 | this.dom = ReactDOM.findDOMNode(this); 65 | } 66 | 67 | componentDidUpdate() { 68 | if (this.followParallax) { 69 | this.doms = this.followParallax.data.map(item => { 70 | return document.getElementById(item.id); 71 | }); 72 | } 73 | } 74 | 75 | componentWillUnmount() { 76 | ticker.clear(this.timeoutID); 77 | ticker.clear(this.delayTimeout); 78 | this.delayTimeout = -1; 79 | this.timeoutID = -1; 80 | } 81 | 82 | onMouseMove = (e) => { 83 | this.domRect = this.dom.getBoundingClientRect(); 84 | this.enterMouse = this.enterMouse || 85 | { x: this.domRect.width / 2, y: this.domRect.height / 2 }; 86 | this.domWH = { 87 | w: this.domRect.width, 88 | h: this.domRect.height, 89 | }; 90 | this.offsetTop = this.domRect.top + currentScrollTop(); 91 | this.offsetLeft = this.domRect.left + currentScrollLeft(); 92 | const mouseXY = { 93 | x: e.pageX - this.offsetLeft, 94 | y: e.pageY - this.offsetTop, 95 | }; 96 | this.setTicker(this.followParallax, mouseXY); 97 | }; 98 | 99 | setTicker = (followParallax, mouseXY, callback = noop) => { 100 | ticker.clear(this.tickerId); 101 | this.tickerId = `bannerElementTicker${Date.now() + Math.random()}`; 102 | const startFrame = ticker.frame; 103 | const startX = this.enterMouse.x; 104 | const startY = this.enterMouse.y; 105 | const duration = followParallax.duration || 450; 106 | const easeFunc = easeTween[followParallax.ease || 107 | 'easeOutQuad']; 108 | const start = typeof followParallax.minMove === 'number' ? 109 | followParallax.minMove : 0.08; 110 | ticker.wake(this.tickerId, () => { 111 | const moment = (ticker.frame - startFrame) * ticker.perFrame; 112 | const ratio = easeFunc(moment, start, 1, duration); 113 | this.enterMouse.x = startX + (mouseXY.x - startX) * ratio; 114 | this.enterMouse.y = startY + (mouseXY.y - startY) * ratio; 115 | this.setFollowStyle(this.domWH); 116 | if (moment >= duration) { 117 | ticker.clear(this.tickerId); 118 | callback(); 119 | } 120 | }); 121 | } 122 | 123 | getFollowMouseMove = () => { 124 | let onMouseMove; 125 | if (this.followParallax) { 126 | if (this.followParallax.delay) { 127 | onMouseMove = !this.delayTimeout ? null : this.state.onMouseMove; 128 | this.delayTimeout = this.delayTimeout || 129 | ticker.timeout(() => { 130 | this.setState({ 131 | onMouseMove: this.onMouseMove, 132 | }); 133 | }, this.followParallax.delay); 134 | } else { 135 | onMouseMove = this.onMouseMove; 136 | } 137 | } 138 | return onMouseMove; 139 | } 140 | 141 | getFollowStyle = (data, domWH) => { 142 | const style = {}; 143 | dataToArray(data.type).forEach(type => { 144 | let mouseData = this.enterMouse.x; 145 | let domData = domWH.w; 146 | const value = data.value; 147 | if ((type.indexOf('y') >= 0 || type.indexOf('Y') >= 0) && type !== 'opacity') { 148 | mouseData = this.enterMouse.y; 149 | domData = domWH.h; 150 | } 151 | const d = (mouseData - domData / 2) / (domData / 2) * value; 152 | const _type = getGsapType(type); 153 | const cssName = isConvert(_type); 154 | if (cssName === 'transform') { 155 | const transform = checkStyleName('transform'); 156 | style[transform] = style[transform] || {}; 157 | style[transform][_type] = stylesToCss(_type, d).trim(); 158 | } else if (cssName === 'filter') { 159 | const filter = checkStyleName('filter'); 160 | style[filter] = style[filter] || {}; 161 | style[filter][_type] = stylesToCss(_type, d).trim(); 162 | } else { 163 | style[cssName] = stylesToCss(_type, d).trim(); 164 | } 165 | }); 166 | return style; 167 | } 168 | 169 | setFollowStyle = (domWH) => { 170 | this.doms.forEach((item, i) => { 171 | if (!item) { 172 | return; 173 | } 174 | const data = this.followParallax.data[i]; 175 | const style = this.getFollowStyle(data, domWH); 176 | Object.keys(style).forEach(key => { 177 | if (typeof style[key] === 'object') { 178 | let styleStr = ''; 179 | Object.keys(style[key]).forEach(_key => { 180 | styleStr += ` ${_key}(${style[key][_key]})`.trim(); 181 | }); 182 | item.style[key] = styleStr; 183 | return; 184 | } 185 | item.style[key] = key.indexOf('backgroundPosition') >= 0 ? 186 | `calc(${data.bgPosition || '0%'} + ${style[key]} )` : style[key]; 187 | }); 188 | }); 189 | }; 190 | 191 | getChildren = () => { 192 | return toArrayChildren(this.props.children).map((item, i) => { 193 | if (item && item.type === BgElement) { 194 | return React.cloneElement(item, { show: this.state.show }); 195 | } 196 | return this.useTagComp ? setAnimCompToTagComp(item, i) : item; 197 | }); 198 | } 199 | 200 | reFollowParallax = () => { 201 | if (!this.domRect) { 202 | return; 203 | } 204 | this.setTicker(this.followParallax, { 205 | x: this.domRect.width / 2 - this.offsetLeft, 206 | y: this.domRect.height / 2 - this.offsetTop, 207 | }, () => { 208 | this.followParallax = null; 209 | }); 210 | } 211 | 212 | animEnd = () => { 213 | const type = this.state.show ? 'enter' : 'leave'; 214 | this.props.callBack(type); 215 | this.setState((_, props) => ({ show: props.show, mouseMoveType: null })); 216 | } 217 | 218 | animChildren = (props, style, bgElem) => { 219 | const { elemOffset, leaveChildHide, ratio, animType: currentAnimType, direction, mouseMoveType, 220 | ease, duration, delay, show, sync, component } = this.props; 221 | if (this.tickerId) { 222 | ticker.clear(this.tickerId); 223 | } 224 | if (this.delayTimeout) { 225 | ticker.clear(this.delayTimeout); 226 | this.delayTimeout = null; 227 | } 228 | style.display = 'block'; 229 | 230 | props.component = component; 231 | style.zIndex = show ? 1 : 0; 232 | const type = show ? 'enter' : 'leave'; 233 | this.useTagComp = (currentAnimType === animType.gridBar || currentAnimType === animType.grid) && 234 | (show === this.state.show || (this.state.show && !show)); 235 | // 状态没改,锁定 children 236 | props.children = !sync && ((show && show !== this.state.show) || (!show && !this.state.show)) ? 237 | bgElem : this.getChildren(); 238 | const childrenToRender = React.createElement(TweenOne, props); 239 | const $ratio = mouseMoveType === 'end' && ratio <= 0.3 ? 1 - ratio : ratio; 240 | const tag = currentAnimType(childrenToRender, 241 | type, 242 | direction, 243 | { 244 | ease, 245 | duration, 246 | delay, 247 | onComplete: this.animEnd, 248 | }, 249 | elemOffset, 250 | leaveChildHide, 251 | $ratio, 252 | this.state.mouseMoveType === 'update', 253 | ); 254 | const { ...tagProps } = tag.props; 255 | if (tagProps.animation) { 256 | tagProps.moment = (tagProps.animation.duration + tagProps.animation.delay) * $ratio || 0; 257 | tagProps.paused = this.state.mouseMoveType === 'update' || $ratio === 1; 258 | } 259 | return React.cloneElement(tag, tagProps); 260 | } 261 | 262 | render() { 263 | const { 264 | prefixCls, 265 | callBack, 266 | animType: propsAnimType, 267 | duration, 268 | delay, 269 | ease, 270 | elemOffset, 271 | followParallax, 272 | show, 273 | type, 274 | direction, 275 | leaveChildHide, 276 | sync, 277 | ratio, 278 | mouseMoveType, 279 | children, 280 | style: propsStyle, 281 | componentProps, 282 | ...props 283 | } = this.props; 284 | const { show: currentShow, mouseMoveType: currentMouseMoveType } = this.state; 285 | const style = { ...propsStyle }; 286 | style.display = show ? 'block' : 'none'; 287 | style.position = 'absolute'; 288 | style.width = '100%'; 289 | if (mouseMoveType !== 'end') { 290 | style[this.transform] = ''; 291 | } 292 | props.style = style; 293 | props.className = `banner-anim-elem ${prefixCls || ''}`.trim(); 294 | const bgElem = toArrayChildren(children).filter(item => 295 | item && item.type.isBannerAnimBgElement) 296 | .map(item => { 297 | return React.cloneElement(item, { show: props.show }); 298 | }); 299 | if (currentShow === show && !currentMouseMoveType || 300 | currentMouseMoveType === 'reChild') { 301 | props.animation = { x: 0, y: 0, type: 'set' }; 302 | if (!show) { 303 | this.enterMouse = null; 304 | return React.createElement(TweenOne, props, bgElem); 305 | } 306 | if (followParallax) { 307 | props.onMouseMove = this.getFollowMouseMove(); 308 | } 309 | return React.createElement(TweenOne, props, 310 | mouseMoveType === 'update' ? bgElem : this.getChildren()); 311 | } 312 | const $props = { ...props, ...componentProps }; 313 | return this.animChildren($props, style, bgElem); 314 | } 315 | } 316 | 317 | Element.propTypes = { 318 | children: PropTypes.any, 319 | style: PropTypes.object, 320 | prefixCls: PropTypes.string, 321 | component: PropTypes.any, 322 | elemOffset: PropTypes.object, 323 | type: PropTypes.string, 324 | animType: PropTypes.func, 325 | ease: PropTypes.string, 326 | duration: PropTypes.number, 327 | delay: PropTypes.number, 328 | direction: PropTypes.string, 329 | callBack: PropTypes.func, 330 | followParallax: PropTypes.any, 331 | show: PropTypes.bool, 332 | leaveChildHide: PropTypes.bool, 333 | sync: PropTypes.bool, 334 | ratio: PropTypes.number, 335 | mouseMoveType: PropTypes.string, 336 | componentProps: PropTypes.object, 337 | }; 338 | Element.defaultProps = { 339 | component: 'div', 340 | componentProps: {}, 341 | callBack: noop, 342 | delay: 0, 343 | }; 344 | 345 | Element.BgElement = polyfill(BgElement); 346 | Element.isBannerAnimElement = true; 347 | export default polyfill(Element); 348 | -------------------------------------------------------------------------------- /src/Thumb.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { toArrayChildren } from './utils'; 4 | 5 | class Thumb extends Component { 6 | getDefaultThumb = () => { 7 | const children = []; 8 | for (let i = 0; i < this.props.length; i++) { 9 | children.push(); 10 | } 11 | return children; 12 | } 13 | 14 | render() { 15 | const { 16 | length, 17 | thumbClick, 18 | active, 19 | defaultBool, 20 | component, 21 | componentProps, 22 | prefixCls, 23 | children: propsChildren, 24 | ...props 25 | } = this.props; 26 | let className = 'banner-anim-thumb'; 27 | const defaultClass = `${className}-default`; 28 | className = `${className} ${prefixCls || ''}`.trim(); 29 | className = !defaultBool ? className : `${className} ${defaultClass}`.trim(); 30 | const children = defaultBool ? this.getDefaultThumb() : propsChildren; 31 | if (length && toArrayChildren(children).length !== length) { 32 | console.warn('The thumbnail length and the images length different.'); // eslint-disable-line 33 | } 34 | const childToRender = toArrayChildren(children).map((item, i) => { 35 | const itemProps = { ...item.props }; 36 | itemProps.onClick = (e) => { 37 | if (e.stopPropagation) { 38 | e.stopPropagation(); 39 | } 40 | thumbClick(i); 41 | }; 42 | itemProps.className = `${itemProps.className || ''} ${active === i ? 'active' : ''}` 43 | .trim(); 44 | return React.cloneElement(item, itemProps); 45 | }); 46 | const $props = { ...props, ...componentProps, className }; 47 | 48 | return React.createElement(component, 49 | $props, 50 | childToRender 51 | ); 52 | } 53 | } 54 | 55 | Thumb.propTypes = { 56 | children: PropTypes.any, 57 | style: PropTypes.object, 58 | prefixCls: PropTypes.string, 59 | component: PropTypes.any, 60 | thumbClick: PropTypes.func, 61 | defaultBool: PropTypes.bool, 62 | length: PropTypes.number, 63 | active: PropTypes.number, 64 | componentProps: PropTypes.object, 65 | }; 66 | Thumb.defaultProps = { 67 | component: 'div', 68 | componentProps: {}, 69 | thumbClick: () => { 70 | }, 71 | }; 72 | Thumb.isBannerAnimThumb = true; 73 | 74 | export default Thumb; 75 | -------------------------------------------------------------------------------- /src/anim.jsx: -------------------------------------------------------------------------------- 1 | import React, { cloneElement } from 'react'; 2 | import { toArrayChildren, switchChildren } from './utils'; 3 | 4 | export default { 5 | across(elem, type, direction, animData, elemOffset, leaveChildHide) { 6 | let _x; 7 | const props = { ...elem.props }; 8 | let children = props.children; 9 | if (type === 'enter') { 10 | _x = direction === 'next' ? '100%' : '-100%'; 11 | } else { 12 | // 时间轴不同,导致中间有空隙, 等修复 twee-one,先加delay 13 | _x = direction === 'next' ? '-100%' : '100%'; 14 | children = toArrayChildren(children).map(switchChildren.bind(this, leaveChildHide)); 15 | } 16 | return cloneElement(elem, { 17 | animation: { 18 | ...animData, 19 | x: _x, 20 | type: type === 'enter' ? 'from' : 'to', 21 | }, 22 | }, children); 23 | }, 24 | vertical(elem, type, direction, animData, elemOffset, leaveChildHide) { 25 | let _y; 26 | const props = { ...elem.props }; 27 | let children = props.children; 28 | if (type === 'enter') { 29 | _y = direction === 'next' ? '-100%' : '100%'; 30 | } else { 31 | // 时间轴不同,导致中间有空隙, 等修复 twee-one,先加delay 32 | _y = direction === 'next' ? '100%' : '-100%'; 33 | children = toArrayChildren(children).map(switchChildren.bind(this, leaveChildHide)); 34 | } 35 | return cloneElement(elem, { 36 | ...props, 37 | animation: { 38 | ...animData, 39 | y: _y, 40 | type: type === 'enter' ? 'from' : 'to', 41 | }, 42 | }, children); 43 | }, 44 | acrossOverlay(elem, type, direction, animData, elemOffset, leaveChildHide) { 45 | let _x; 46 | const props = { ...elem.props }; 47 | let children = props.children; 48 | if (type === 'enter') { 49 | _x = direction === 'next' ? '100%' : '-100%'; 50 | } else { 51 | _x = direction === 'next' ? '-20%' : '20%'; 52 | children = toArrayChildren(children).map(switchChildren.bind(this, leaveChildHide)); 53 | } 54 | return cloneElement(elem, { 55 | ...props, 56 | animation: { 57 | ...animData, 58 | x: _x, 59 | type: type === 'enter' ? 'from' : 'to', 60 | }, 61 | }, children); 62 | }, 63 | verticalOverlay(elem, type, direction, animData, elemOffset, leaveChildHide) { 64 | let _y; 65 | const props = { ...elem.props }; 66 | let children = props.children; 67 | if (type === 'enter') { 68 | _y = direction === 'next' ? '-100%' : '100%'; 69 | } else { 70 | _y = direction === 'next' ? '20%' : '-20%'; 71 | children = toArrayChildren(children).map(switchChildren.bind(this, leaveChildHide)); 72 | } 73 | return cloneElement(elem, { 74 | ...props, 75 | animation: { 76 | ...animData, 77 | y: _y, 78 | type: type === 'enter' ? 'from' : 'to', 79 | }, 80 | }, children); 81 | }, 82 | gridBar(elem, type, direction, animData, elemOffset, leaveChildHide, ratio, paused) { 83 | const props = { ...elem.props }; 84 | const animChild = []; 85 | const gridNum = 10; 86 | const girdSize = 100 / gridNum; 87 | 88 | let _y; 89 | const children = props.children; 90 | if (type === 'enter') { 91 | _y = direction === 'next' ? '-100%' : '100%'; 92 | } else { 93 | _y = direction === 'next' ? '100%' : '-100%'; 94 | } 95 | const moment = ratio * (animData.duration + animData.delay + gridNum * 50 - (type === 'enter' ? 50 : 0)) || 0; 96 | for (let i = 0; i < gridNum; i++) { 97 | const style = { ...props.style }; 98 | style.width = `${girdSize + 0.1}%`; 99 | style.left = `${i * girdSize}%`; 100 | style.position = 'absolute'; 101 | style.overflow = 'hidden'; 102 | const _style = { ...props.style }; 103 | _style.width = `${elemOffset.width}px`; 104 | _style.height = `${elemOffset.height}px`; 105 | _style.float = 'left'; 106 | _style.position = 'relative'; 107 | _style.left = `${-i * girdSize / 100 * elemOffset.width}px`; 108 | _style.overflow = 'hidden'; 109 | const animProps = { ...props }; 110 | animProps.style = _style; 111 | const delay = (direction === 'next' ? i : gridNum - i) * 50 + (type === 'enter' ? 0 : 50) + (animData.delay || 0); 112 | animProps.animation = { 113 | ...animData, 114 | y: _y, 115 | type: type === 'enter' ? 'from' : 'to', 116 | key: type, 117 | direction, 118 | delay, 119 | i, 120 | onComplete: i === (direction === 'next' ? gridNum - 1 : 0) ? 121 | animData.onComplete : null, 122 | }; 123 | animProps.paused = paused; 124 | animProps.moment = moment; 125 | const mask = (
    126 | {cloneElement(elem, animProps, children)} 127 |
    ); 128 | animChild.push(mask); 129 | } 130 | const animSlot = (
    131 | {animChild} 132 |
    ); 133 | const _props = { ...elem.props }; 134 | _props.children = animSlot; 135 | return cloneElement(elem, { ..._props, animation: { x: 0, y: 0, type: 'set' } }); 136 | }, 137 | grid(elem, type, direction, animData, elemOffset, leaveChildHide, ratio, paused) { 138 | const props = { ...elem.props }; 139 | const animChild = []; 140 | const gridNum = 10; 141 | const gridWidth = elemOffset.width / gridNum; 142 | const gridNumH = Math.ceil(elemOffset.height / gridWidth); 143 | const _delay = (gridNum - 1) * 50 + (gridNumH - 1) * 50; 144 | if (type === 'leave') { 145 | props.animation = { 146 | ...animData, 147 | duration: _delay + animData.duration, 148 | }; 149 | props.moment = ((animData.delay || 0) + _delay + animData.duration) * ratio || 0; 150 | props.paused = paused; 151 | return React.cloneElement(elem, props); 152 | } 153 | const moment = ratio * (animData.duration + animData.delay + _delay) || 0; 154 | for (let i = 0; i < gridNum * gridNumH; i++) { 155 | // mask样式 156 | const style = { ...props.style }; 157 | style.position = 'absolute'; 158 | style.overflow = 'hidden'; 159 | style.width = `${gridWidth + 1}px`; 160 | style.height = `${gridWidth + 1}px`; 161 | style.left = i % gridNum * gridWidth; 162 | style.top = Math.floor(i / gridNum) * gridWidth; 163 | style.opacity = 0; 164 | // clone 的样式 165 | const _style = { ...props.style }; 166 | _style.width = `${elemOffset.width}px`; 167 | _style.height = `${elemOffset.height}px`; 168 | _style.position = 'relative'; 169 | _style.left = -i % gridNum * gridWidth; 170 | _style.top = -Math.floor(i / gridNum) * gridWidth; 171 | _style.overflow = 'hidden'; 172 | props.style = _style; 173 | let delay = direction === 'next' ? i % gridNum * 50 + Math.floor(i / gridNum) * 50 : 174 | (gridNum - 1 - i % gridNum) * 50 + (gridNumH - 1 - Math.floor(i / gridNum)) * 50; 175 | delay += animData.delay || 0; 176 | const length = direction === 'next' ? gridNum * gridNumH - 1 : 0; 177 | const animation = { 178 | ...animData, 179 | opacity: 1, 180 | delay, 181 | onComplete: i === length ? animData.onComplete : null, 182 | }; 183 | const mask = ( 184 | 191 | {cloneElement(elem, props)} 192 | ); 193 | animChild.push(mask); 194 | } 195 | const _props = { ...elem.props }; 196 | _props.children = animChild; 197 | return cloneElement(elem, { ..._props, animation: { x: 0, y: 0, type: 'set' } }); 198 | }, 199 | }; 200 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // export this package's api 2 | import { polyfill } from 'react-lifecycles-compat'; 3 | import BannerAnim from './BannerAnim'; 4 | import Arrow from './Arrow'; 5 | import Element from './Element'; 6 | import Thumb from './Thumb'; 7 | import animType from './anim'; 8 | import { setAnimCompToTagComp, switchChildren } from './utils'; 9 | 10 | BannerAnim.Arrow = Arrow; 11 | BannerAnim.Element = Element; 12 | BannerAnim.Thumb = Thumb; 13 | BannerAnim.animType = animType; 14 | BannerAnim.setAnimCompToTagComp = setAnimCompToTagComp; 15 | BannerAnim.switchChildren = switchChildren; 16 | 17 | export default polyfill(BannerAnim); 18 | 19 | export { 20 | Arrow, 21 | Element, 22 | Thumb, 23 | animType, 24 | setAnimCompToTagComp, 25 | switchChildren, 26 | } 27 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | export function toArrayChildren(children) { 5 | const ret = []; 6 | React.Children.forEach(children, c => { 7 | ret.push(c); 8 | }); 9 | return ret; 10 | } 11 | 12 | export function dataToArray(vars) { 13 | if (!vars && vars !== 0) { 14 | return []; 15 | } 16 | if (Array.isArray(vars)) { 17 | return vars; 18 | } 19 | return [vars]; 20 | } 21 | 22 | export function setAnimCompToTagComp(item, i) { 23 | if (!item) { 24 | return null; 25 | } 26 | const itemProps = item.props; 27 | if (!itemProps.component) { 28 | return item; 29 | } 30 | const props = {}; 31 | props.key = item.key || i; 32 | // dom global attributes 33 | const domAttrArray = [ 34 | 'accesskey', 'classname', 'contenteditable', 'contextmenu', 'dir', 'draggable', 35 | 'dropzone', 'hidden', 'id', 'lang', 'spellcheck', 'style', 'tabindex', 'title', 36 | ]; 37 | Object.keys(itemProps).forEach(key => { 38 | if (domAttrArray.indexOf(key.toLocaleLowerCase()) >= 0 || key.match('data-')) { 39 | props[key] = itemProps[key]; 40 | } 41 | }); 42 | return React.createElement(itemProps.component, props, itemProps.children); 43 | } 44 | setAnimCompToTagComp.propTypes = { 45 | key: PropTypes.string, 46 | }; 47 | 48 | export function currentScrollTop() { 49 | return window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop; 50 | } 51 | 52 | export function currentScrollLeft() { 53 | return window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft; 54 | } 55 | 56 | export function windowHeight() { 57 | return window.innerHeight || 58 | document.documentElement.clientHeight || document.body.clientHeight; 59 | } 60 | 61 | export function switchChildren(hideProps, item) { 62 | if (!hideProps) { 63 | return item; 64 | } 65 | if (item.type.isTweenOne) { 66 | return React.cloneElement(item, { reverse: true }); 67 | } 68 | return React.cloneElement(item, item.props, null); 69 | } 70 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | import 'core-js/es6/map'; 2 | import 'core-js/es6/set'; 3 | import React from 'react'; 4 | import ReactDom from 'react-dom'; 5 | import PropTypes from 'prop-types'; 6 | import expect from 'expect.js'; 7 | import BannerAnim from 'rc-banner-anim'; 8 | import TestUtils from 'react-dom/test-utils'; 9 | import '../assets/index.less'; 10 | import '../examples/assets/index.less'; 11 | 12 | const { Element, Arrow, Thumb } = BannerAnim; 13 | const BgElement = Element.BgElement; 14 | describe('rc-banner-anim', () => { 15 | let div; 16 | 17 | function createBannerAnimInstance(props = {}) { 18 | class BannerAnimExample extends React.PureComponent { 19 | static propTypes = { 20 | showArrowAndThumb: PropTypes.bool, 21 | } 22 | render() { 23 | const { showArrowAndThumb, ...cProps } = this.props; 24 | return ( 25 | 26 | 27 | 36 | test text 37 | 38 | 39 | 48 | test text 49 | 50 | {showArrowAndThumb && [ 51 | left, 52 | next, 53 | 54 |
    55 |
    56 | 57 | ]} 58 | 59 | ); 60 | } 61 | } 62 | return ReactDom.render(, div); 63 | } 64 | 65 | beforeEach(() => { 66 | div = document.createElement('div'); 67 | document.body.appendChild(div); 68 | }); 69 | 70 | afterEach(() => { 71 | try { 72 | ReactDom.unmountComponentAtNode(div); 73 | document.body.removeChild(div); 74 | } catch (e) { 75 | console.log(e); // eslint-disable-line no-console 76 | } 77 | }); 78 | 79 | it('should render children', () => { 80 | const instance = createBannerAnimInstance({ 81 | type: 'gridBar', 82 | }); 83 | const children = TestUtils.scryRenderedDOMComponentsWithTag(instance, 'div'); 84 | const childrenSpan = TestUtils.scryRenderedDOMComponentsWithTag(instance, 'span'); 85 | // banner-anim elem bg prev next thumb 6个; 86 | expect(children.length).to.be(8); 87 | // test text 两个点是 span 88 | expect(childrenSpan.length).to.be(3); 89 | }); 90 | 91 | it('should render arrow and thubm', () => { 92 | const instance = createBannerAnimInstance({ 93 | type: 'gird', 94 | showArrowAndThumb: true, 95 | }); 96 | const children = TestUtils.scryRenderedDOMComponentsWithTag(instance, 'div'); 97 | expect(children.length).to.be(10); 98 | }); 99 | 100 | it('banner animation initShow', () => { 101 | const instance = createBannerAnimInstance({ 102 | initShow: 1, 103 | type: 'across', 104 | }); 105 | const children = TestUtils.scryRenderedDOMComponentsWithClass(instance, 'banner-anim-elem'); 106 | setTimeout(() => { 107 | expect(children[0].style.display).to.be('none'); 108 | expect(children[1].style.display).to.be('block'); 109 | }); 110 | }); 111 | 112 | it('banner animation autoplay', (done) => { 113 | const instance = createBannerAnimInstance({ 114 | autoPlay: true, 115 | autoPlaySpeed: 1000, 116 | type: 'vertical', 117 | }); 118 | let children = TestUtils.scryRenderedDOMComponentsWithClass(instance, 'banner-anim-elem'); 119 | expect(children[0].style.display).to.be('block'); 120 | expect(children[1].style.display).to.be('none'); 121 | setTimeout(() => { 122 | children = TestUtils.scryRenderedDOMComponentsWithClass(instance, 'banner-anim-elem'); 123 | expect(children[0].style.display).to.be('block'); 124 | expect(children[1].style.display).to.be('block'); 125 | done(); 126 | }, 1300); 127 | }); 128 | }); 129 | -------------------------------------------------------------------------------- /tests/test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import BannerAnim, { animType, Arrow, Element, setAnimCompToTagComp, switchChildren, Thumb } from '../typings'; 4 | /* 5 | BannerAnim.switchChildren; 6 | BannerAnim.setAnimCompToTagComp; 7 | BannerAnim.animType; 8 | */ 9 | 10 | function Demo() { 11 | return ( 12 | 13 | 14 | 15 |
    test
    16 |
    17 | text 18 | text 19 |
    20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "build/dist", 4 | "module": "esnext", 5 | "target": "es2016", 6 | "lib": ["es6", "dom"], 7 | "sourceMap": true, 8 | "jsx": "react", 9 | "allowSyntheticDefaultImports": true, 10 | "moduleResolution": "node", 11 | "rootDirs": ["/src", "./typings"], 12 | "forceConsistentCasingInFileNames": true, 13 | "noImplicitReturns": true, 14 | "suppressImplicitAnyIndexErrors": true, 15 | "noUnusedLocals": true, 16 | "allowJs": true, 17 | "experimentalDecorators": true 18 | }, 19 | "include": ["./src"], 20 | "exclude": [ 21 | "node_modules", 22 | "build", 23 | "scripts", 24 | "acceptance-tests", 25 | "webpack", 26 | "jest", 27 | "src/setupTests.ts", 28 | "tslint:latest", 29 | "tslint-config-prettier" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest", "tslint-react", "tslint-config-prettier"], 3 | "rules": { 4 | "no-var-requires": false, 5 | "no-submodule-imports": false, 6 | "object-literal-sort-keys": false, 7 | "jsx-no-lambda": false, 8 | "no-implicit-dependencies": false, 9 | "no-console": false 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /typings/Arrow.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export interface IProps extends React.HTMLAttributes { 4 | prefixCls?: string; 5 | arrowType: 'prev' | 'next'; 6 | component?: string | React.ReactNode; 7 | componentProps?: {}; 8 | } 9 | 10 | export default class Arrow extends React.Component> { 11 | 12 | } -------------------------------------------------------------------------------- /typings/BgElement.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { IStyleAnimProps } from 'rc-tween-one/typings/AnimObject'; 3 | 4 | export interface IProps extends React.HTMLAttributes { 5 | scrollParallax?: IStyleAnimProps; 6 | videoResize?: boolean; 7 | component?: string | React.ReactNode; 8 | componentProps?: {}; 9 | } 10 | 11 | export default class BgElement extends React.Component> { 12 | 13 | } -------------------------------------------------------------------------------- /typings/Element.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { IEaseType, IStyleAnimProps } from 'rc-tween-one/typings/AnimObject'; 3 | import BgElement from './BgElement'; 4 | 5 | export interface IDataType { 6 | key?: string; 7 | value?: number; 8 | type?: IStyleAnimProps | IStyleAnimProps[]; 9 | bgPosition?: string; 10 | } 11 | 12 | export interface IFollowType { 13 | delay?: number; 14 | ease?: IEaseType; 15 | minMover?: number; 16 | data: IDataType; 17 | } 18 | 19 | export interface IProps extends React.HTMLAttributes { 20 | leaveChildHide?: boolean; 21 | sync?: boolean; 22 | prefixCls?: string; 23 | followParallax?: IFollowType; 24 | component?: string | React.ReactNode; 25 | componentProps?: {}; 26 | } 27 | 28 | export default class Element extends React.Component> { 29 | static BgElement: typeof BgElement; 30 | } 31 | -------------------------------------------------------------------------------- /typings/Thumb.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export interface IProps extends React.HTMLAttributes { 4 | children: React.ReactNode; 5 | prefixCls?: string; 6 | component?: string | React.ReactNode; 7 | componentProps?: {}; 8 | } 9 | 10 | export default class Thumb extends React.Component> { 11 | 12 | } -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for rc-banner-anim 2.0 2 | // Project: https://github.com/react-component/banner-anim 3 | // Definitions by: jljsj33 4 | // Definitions: https://github.com/react-component/banner-anim 5 | import * as React from 'react'; 6 | 7 | import { IEaseType } from 'rc-tween-one/typings/AnimObject'; 8 | 9 | import Arrow from './Arrow'; 10 | import Thumb from './Thumb'; 11 | import Element from './Element'; 12 | 13 | export type Omit = Pick>; 14 | 15 | type ITypeString = 'across' | 'vertical' | 'acrossOverlay' | 'verticalOverlay' | 'gridBar' | 'grid'; 16 | 17 | export declare type IType = ITypeString | ITypeString[]; 18 | 19 | export interface IProps extends Omit, 'onChange'> { 20 | prefixCls?: string; 21 | arrow?: boolean; 22 | thumb?: boolean; 23 | initShow?: number; 24 | type?: IType; 25 | duration?: number; 26 | delay?: number; 27 | ease?: IEaseType; 28 | autoPlay?: boolean; 29 | autoPlaySpeed?: number; 30 | sync?: boolean; 31 | dragPlay?: boolean; 32 | onChange?: (type: string, current: number) => void; 33 | component?: string | React.ReactNode; 34 | } 35 | 36 | export declare function setAnimCompToTagComp(item: React.ReactElement, i?: number): void; 37 | export declare function switchChildren(hideProps: boolean, item: React.ReactElement): void; 38 | export declare const animType: {}; 39 | 40 | export default class RcBannerAnim extends React.Component> { 41 | static Arrow: typeof Arrow; 42 | static Thumb: typeof Thumb; 43 | static Element: typeof Element; 44 | static setAnimCompToTagComp: typeof setAnimCompToTagComp; 45 | static animType: typeof animType; 46 | static switchChildren: typeof switchChildren; 47 | } 48 | 49 | export { 50 | Arrow, 51 | Thumb, 52 | Element, 53 | }; 54 | 55 | --------------------------------------------------------------------------------