React之動畫實現

React之動畫實現

一,介紹與需求

1.1,介紹

1,Ant Motioncss

Ant Motion可以快速在 React 框架中使用動畫。在 React 框架下,只須要一段簡單的代碼就能夠實現動畫效果react

2,SVG npm

  • SVG 指可伸縮矢量圖形 (Scalable Vector Graphics)
  • SVG 用來定義用於網絡的基於矢量的圖形
  • SVG 使用 XML 格式定義圖形
  • SVG 圖像在放大或改變尺寸的狀況下其圖形質量不會有所損失
  • SVG 是萬維網聯盟的標準
  • SVG 與諸如 DOM 和 XSL 之類的 W3C 標準是一個總體

1.2,需求

提升網站的交互效果,提升用戶體驗。界面動效能增強用戶認知且增長活力。網絡

二,基於Ant Motion的react動畫

2.1,動畫效果

1,snow掉落效果app

2,彙集與散開框架

 2.2,動畫實現方式

以掉落效果爲例:less

 1 import React from 'react';
 2 import Snow from './Snow';
 3 import './index.less';
 4 class App extends React.Component {
 5   constructor() {
 6     super(...arguments);
 7     this.state = {
 8       show: true,
 9     };
10   }
11   onEnd = () => {
12     this.setState({
13       show: false,
14     });
15   }
16   render() {
17     const children = Array(5).fill(1).map((c, i) => (
18       <div key={i} className="addMoneyAnim" style={{ animationDelay: `${-Math.random() * 0.6}s` }} />
19     ));
20     return (
21       <div className="snow-demo-wrapper" >
22       <div className="snow-demo">
23        
24           <Snow onEnd={this.onEnd} >
25             {children}
26           </Snow>
27         
28       </div>
29     </div>
30     );
31   }
32 }
33 
34 export default App;

組件snow代碼:dom

  1   import React from 'react';
  2   import TweenOne from 'rc-tween-one';
  3   import BezierPlugin from 'rc-tween-one/lib/plugin/BezierPlugin';
  4   import PropTypes from 'prop-types';
  5   
  6   import './index.less';
  7   
  8   TweenOne.plugins.push(BezierPlugin);
  9   
 10   class Snow extends React.Component {
 11     static propTypes = {
 12       children: PropTypes.any,
 13       className: PropTypes.string,
 14       prefixCls: PropTypes.string,
 15       amount: PropTypes.number,
 16       repeat: PropTypes.number,
 17       ease: PropTypes.string,
 18       startArea: PropTypes.object,
 19       endArea: PropTypes.object,
 20       startDelayRandom: PropTypes.number,
 21       basicToDuration: PropTypes.number,
 22       randomToDuration: PropTypes.number,
 23       rotateRandom: PropTypes.number,
 24       bezierSegmentation: PropTypes.number,
 25       onEnd: PropTypes.func,
 26     }
 27     static defaultProps = {
 28       prefixCls: 'snow',
 29       amount: 10,
 30       repeat: 0,
 31       ease: 'linear',
 32       startArea: {
 33         x: 0, y: -200, width: '100%', height: 50,
 34       },
 35       endArea: {
 36         x: -200, y: '100%', width: '120%', height: 100,
 37       },
 38       basicToDuration: 1200,
 39       randomToDuration: 800,
 40       startDelayRandom: 800,
 41       rotateRandom: 180,
 42       bezierSegmentation: 2,
 43       onEnd: () => { },
 44     };
 45   
 46     constructor(props) {
 47       super(props);
 48       this.state = {
 49         children: null,
 50       };
 51     }
 52     componentDidMount() {
 53       this.setChilrenToState();
 54     }
 55   
 56     onAnimEnd = () => {
 57       this.animEnd += 1;
 58       if (this.animEnd >= this.props.amount) {
 59         this.animEnd = 0;
 60         if (this.props.onEnd) {
 61           this.props.onEnd();
 62         }
 63       }
 64     }
 65   
 66     setChilrenToState() {
 67       const children = this.getChildrenToRender();
 68       this.setState({
 69         children,
 70       });
 71     }
 72   
 73     getChildrenToRender = () => {
 74       const {
 75         bezierSegmentation, basicToDuration, randomToDuration,
 76         amount, ease, startDelayRandom, repeat, rotateRandom,
 77       } = this.props;
 78       const children = React.Children.toArray(this.props.children);
 79       const rect = this.wrapperDom.getBoundingClientRect();
 80       const startArea = this.dataToNumber(this.props.startArea, rect);
 81       const endArea = this.dataToNumber(this.props.endArea, rect);
 82       return Array(amount).fill(1).map((k, i) => {
 83         const item = children[Math.floor(Math.random() * children.length)];
 84         const vars = Array(bezierSegmentation).fill(1).map((c, j) => {
 85           const hegiht = endArea.y - startArea.y - startArea.height;
 86           const y = (hegiht / bezierSegmentation) * (j + 1);
 87           const x = Math.random() * (Math.max(startArea.width, endArea.width)
 88             + Math.min(startArea.x, endArea.x));
 89           // console.log(hegiht, startArea, endArea, y);
 90           return {
 91             y,
 92             x,
 93           };
 94         });
 95         const delay = Math.random() * startDelayRandom;
 96         const animation = {
 97           bezier: {
 98             type: 'soft',
 99             autRotate: true,
100             vars,
101           },
102           ease,
103           repeat,
104           repeatDelay: delay,
105           delay,
106           duration: basicToDuration + Math.random() * randomToDuration,
107           onComplete: this.onAnimEnd,
108         };
109         const style = {
110           transform: `translate(${Math.random() * (startArea.width) + startArea.x}px, ${
111             Math.random() * (startArea.height) + startArea.y
112           }px)`,
113         };
114         const child = rotateRandom ? (
115           <TweenOne
116             className="snowRotate"
117             style={{ transform: `rotate(${Math.random() * rotateRandom}deg)` }}
118             animation={{
119               rotate: 0,
120               duration: animation.duration * 4 / 5,
121               delay: animation.delay,
122               repeat: animation.repeat,
123             }}
124           >
125             {item}
126           </TweenOne>
127         ) : item;
128         return (
129           <TweenOne
130             animation={animation}
131             style={style}
132             key={`${item}-${i.toString()}`}
133             className="snowChild"
134           >
135             {child}
136           </TweenOne>
137         );
138       });
139     }
140     dataToNumber = (obj, rect) => {
141       const toNumber = (v, full) => {
142         if (typeof v === 'number') {
143           return v;
144         }
145         const unit = v.replace(/[0-9|.]/g, '');
146         switch (unit) {
147           case '%':
148             return parseFloat(v) * full / 100;
149           case 'em':
150             return parseFloat(v) * 16;
151           default:
152             return null;
153         }
154       };
155       return {
156         x: toNumber(obj.x, rect.width),
157         y: toNumber(obj.y, rect.height),
158         width: toNumber(obj.width, rect.width),
159         height: toNumber(obj.height, rect.height),
160       };
161     }
162     animEnd = 0;
163     render() {
164       const { prefixCls, ...props } = this.props;
165       const { children } = this.state;
166       [
167         'amount',
168         'repeat',
169         'ease',
170         'startArea',
171         'endArea',
172         'basicToDuration',
173         'randomToDuration',
174         'startDelayRandom',
175         'bezierSegmentation',
176         'rotateRandom',
177         'onEnd',
178       ].forEach(k => delete props[k]);
179       const className = `${prefixCls}${props.className ? ` ${props.className}` : ''}`;
180       return (
181         <div
182           {...props}
183           ref={(c) => {
184             this.wrapperDom = c;
185           }}
186           className={className}
187         >
188           {children}
189         </div>
190       );
191     }
192   }
193   export default Snow
194   

樣式代碼:svg

 1 .snow-demo-wrapper {
 2   background: #DFEAFF;
 3   overflow: hidden;
 4   height: 500px;
 5   display: flex;
 6   align-items: center;
 7   position: relative;
 8 }
 9 
10 .snow-demo {
11   width: 300px;
12   height: 90%;
13   margin: auto;
14   position: relative;
15   background-image: url(https://gw.alipayobjects.com/zos/rmsportal/dNpuKMDHFEpMGrTxdLVR.jpg);
16   background-position: top;
17   background-size: 100% auto;
18   box-shadow: 0 0 32px rgba(0, 0, 0, 0.15);
19 }
20 
21 .snow {
22   width: 100%;
23   height: 100%;
24   position: absolute;
25   top: 0;
26   overflow: hidden;
27 }
28 
29 .snowChild {
30   position: absolute;
31   top: 0;
32   left: 0;
33 }
34 
35 .snowRotate {
36   transform-origin: center center;
37 } 

 2.3,動畫分類

1,單元素動畫rc-tween-oneflex

1 cnpm install rc-tween-one --save

2,css樣式動畫rc-animate

1 cnpm install rc-animate --save

3,QueueAnim進出場動畫

1 cnpm install rc-queue-anim --save

4,TextyAnim文字動畫

1 cnpm install rc-texty --save

5,ScrollAnim頁面滾動動畫

1 cnpm install rc-scroll-anim --save

6,Banner動畫

1 cnpm install rc-banner-anim --save

詳細動畫實例可查看官網

三,基於svg的react動畫

3.1,動畫效果

鼠標移入動畫執行,鼠標移出動畫中止

1,縱隊動畫

2,俄羅斯方塊

3,座標動畫

 

3.2,動畫實現方式

以縱隊動畫爲例以下代碼:

 1 import React from 'react';
 2 import Column from '../technology/Column';//實現動畫的svg組件
 3 
 4 export default class ReactAnimation extends React.Component {
 5   constructor(props) {
 6     super(props);
 7     this.state = {
 8       hover: null,//是否有鼠標的移入
 9     };
10   }
11 
12   onMouseEnter = (hover) => {//鼠標移入
13     this.setState({
14       hover,
15     });
16   };
17   onMouseLeave = () => {//鼠標移出
18     this.setState({
19       hover: null,
20     });
21   };
22   render() {
23 
24     return (
25       <div>
26          <div
27             onMouseEnter={() => { this.onMouseEnter(1); }}
28             onMouseLeave={this.onMouseLeave}
29           >
30          <div>
31               {Column && React.createElement(Column, {
32                 hover:this.state.hover === 1,
33               })}
34             </div>
35             </div>
36       </div>
37     );
38   }
39 }

React.createElement(): 根據指定的第一個參數建立一個React元素。

1 React.createElement(
2   type,
3   [props],
4   [...children]
5 )

第一個參數是必填,傳入的是似HTML標籤名稱,如: ul, li 
第二個參數是選填,表示的是屬性,如: className 
第三個參數是選填, 子節點,如: 要顯示的文本內容

SVG配置組件Column.jsx:

 1 import React from 'react';
 2 import TweenOne from 'rc-tween-one';//引入動畫插件
 3 
 4 function TweenOneG(props) {
 5   function getAnimation() {
 6     return props.animation.map((item, i) => {
 7       return { ...item, duration: 400 };
 8     });
 9   }
10   return (
11     <TweenOne
12       component="g"
13       {...props}
14       animation={
15         props.animation ?
16           getAnimation() :
17           null
18       }
19     />);
20 }
21 
22 export default class Column extends React.PureComponent {
23   render() {
24     const { hover } = this.props;
25     return (
26       <svg width="328px" height="150px" viewBox="0 0 328 150">
27         <defs>
28           <linearGradient x1="50%" y1="3.05125957%" x2="50%" y2="157.404891%" id="linearGradient-1">
29             <stop stopColor="#2898FF" offset="0%" />
30           </linearGradient>
31         </defs>
32         <g id="Page-1" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
33           <TweenOneG animation={hover ? [{ y: -20 }, { y: -10 }, { y: -30 }] : this.default1Anim}>
34             <g id="Group-33" transform="translate(0.000000, 116.000000)">
35               <rect id="Rectangle-15" fill="#1890FF" opacity="0.03" x="0" y="2" width="20" height="145" />
36             </g>
37           </TweenOneG>
38         </g>
39       </svg>);
40   }
41 }

 上面展現的只是部分代碼,如需完整的代碼,請先留言評論加關注

相關文章
相關標籤/搜索