React之动画实现
一,介绍与需求
1.1,介绍
1,Ant Motion
Ant Motion能够快速在 React 框架中使用动画。在 React 框架下,只需要一段简单的代码就可以实现动画效果
2,SVG
- SVG 指可伸缩矢量图形 (Scalable Vector Graphics)
- SVG 用来定义用于网络的基于矢量的图形
- SVG 使用 XML 格式定义图形
- SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失
- SVG 是万维网联盟的标准
- SVG 与诸如 DOM 和 XSL 之类的 W3C 标准是一个整体
1.2,需求
提高网站的交互效果,提高用户体验。界面动效能加强用户认知且增加活力。
二,基于Ant Motion的react动画
2.1,动画效果
1,snow掉落效果
2,聚集与散开
2.2,动画实现方式
以掉落效果为例:
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 19 )); 20 return ( 212230 ); 31 } 32 } 33 34 export default App;23 2429this.onEnd} > 25 {children} 26 27 28
组件snow代码:
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 127 ) : item; 128 return ( 129 <TweenOne 130 animation={animation} 131 style={style} 132 key={`${item}-${i.toString()}`} 133 className="snowChild" 134 > 135 {child} 136 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