自写一个文字按行渐入动画组件

今天,在我原先的AnimationList组件上增加了个新功能,话不多说,直接上代码(由于网络垃圾进不去github,就直接粘代码了)。

index.tsx文件 :

  1 import React,{Fragment} from 'react';
  2 import styles from './index.less'; 
  3 import undefined from '@/e2e/__mocks__/antd-pro-merge-less';
  4 export interface State {
  5     list:Array<any>,
  6     cacheList:Array<any>,
  7     eventIF:boolean,
  8 }
  9 export interface Props {
 10      style?:any,
 11      styleSon?:any,
 12      val?:valFrom,
 13      dataSource?:Array<dataSource>,
 14      onClickSon?:any,
 15      onMouseEnterSon?:any,
 16      onMouseLeaveSon?:any,
 17      text?:text,
 18 }
 19 interface text{
 20     textEffect:boolean,//是否开启文本动画
 21     testDataSource:string,//文本
 22     typesetting?:'transverse' | 'vertical',//排版方向
 23     separate?:string,//分隔符
 24     interval?:number //行或列间隔
 25     rowDelay?:number //行延时
 26 }
 27 interface valFrom{
 28     type?:TYPE|string,//动画类型
 29     direction?:DIRECTION|string,//方向
 30     time?:number,//时间 单位s
 31     delay?:number,//动画执行前的延时时间 单位s
 32     sonDelay?:number,//列表子项动画延时
 33     domId?:string,//事件绑定dom id
 34     event?:EVENT|string,//动画执行事件
 35     hideModel?:boolean//背景是否显示
 36     typesetting?:'transverse' | 'vertical',//排版方向
 37 } 
 38 export const enum TYPE{FADEIN}
 39 export const enum DIRECTION{TOP,BUTTOM,LEFT,REGIST,TOPLEFT,TOPREGIST,BUTTOMLEFT,BUTTOMREGIST}
 40 export const enum EVENT{CLICK,MOUSEENTER}
 41 interface dataSource{keys:any,title:any,style?:any}
 42 export class Father extends React.Component<Props, State> {
 43     constructor(props: Props) {
 44         super(props);
 45         this.state = { 
 46             list:[],//列表项
 47             cacheList:[],//暂时存储,观望是否绑定dom
 48             eventIF:false,//是否触发了event事件
 49 
 50         };
 51         if(this.props.val !== undefined){
 52             const val:valFrom = this.props.val;
 53             if(this.props.val.type != undefined && !(val.type===TYPE.FADEIN || val.type==="fadeIn")){
 54                 throw Error(`type定义错误:错误值为 ${val.type},type取值为{enum:TYPE,'fadeIn'}`,);
 55             }
 56             if(this.props.val.direction != undefined && !(val.direction === DIRECTION.TOP || val.direction === DIRECTION.BUTTOM || 
 57                 val.direction === DIRECTION.LEFT||val.direction === DIRECTION.REGIST || val.direction === DIRECTION.TOPLEFT ||
 58                 val.direction === DIRECTION.TOPREGIST || val.direction === DIRECTION.BUTTOMLEFT || val.direction === DIRECTION.BUTTOMREGIST ||
 59                 val.direction === 'top' || val.direction === 'buttom' || val.direction=== 'left' || val.direction === 'regist' ||
 60                 val.direction=== 'topLeft' || val.direction === 'topRegist' || val.direction === 'buttomLeft' || val.direction === 'buttomRegist')){
 61                 throw Error(`direction定义错误:错误值为 ${val.direction},direction取值为{enum:DIRECTION,'top','buttom','left','regist',
 62                 'topLeft','topRegist','buttomLeft','buttomRegist'}`);
 63             }
 64             window.onload = function(){
 65                 if(val.domId !== undefined){    
 66                     if(document.getElementById(val.domId)===undefined || document.getElementById(val.domId)===null){
 67                         throw Error(`指定id的DOM元素不存在!`,);
 68                     }
 69                     if(val.event === undefined){
 70                         console.warn(`指定DOM元素情况下未指定绑定事件event!`);
 71                     }
 72                 } 
 73             }
 74             if(val.event !== undefined){
 75                 if(!(val.event === EVENT.CLICK || val.event === EVENT.MOUSEENTER  || val.event === 'click' ||
 76                 val.event === 'mouseEnter')){
 77                     throw Error(`event定义错误:错误值为 ${val.event},event取值为{enum:EVENT,'click','mouseEnter'}`,);
 78                 }
 79                 if(val.domId === undefined){
 80                     console.warn(`绑定事件后未指定DOM元素!`);
 81                 }
 82             }
 83         }  
 84     }
 85     isWidth=(strs:Array<any>):number=>{
 86         let str : Array<string> = [];
 87         for(let i=0;i<strs.length;i++){
 88             if(strs[i].type!==undefined && strs[i].type===Son){
 89                 str.push(strs[i].props.children);
 90             }
 91         }
 92         let max:number = 0;
 93         let reg:RegExp =  /[\u4E00-\u9FA5\uF900-\uFA2D]/i;
 94         str.forEach(element => {
 95             let forMax = 0;
 96             for(let i=0;i<element.length;i++){
 97                 if(reg.test(element.charAt(i))){
 98                     forMax+=2;
 99                 }else{
100                     forMax++;
101                 }
102             }
103             if(forMax > max){
104                 max = forMax;
105             }
106         });
107         return max;
108     }
109     isWidth1=(maxWidth:number,data:Array<dataSource>):number=>{
110         let max:number = maxWidth;
111         let reg:RegExp =  /[\u4E00-\u9FA5\uF900-\uFA2D]/i;
112         data.forEach(element => {
113             let forMax = 0;
114             for(let i=0;i<element.title.length;i++){
115                 if(reg.test(element.title.charAt(i))){
116                     forMax+=2;
117                 }else{
118                     forMax++;
119                 }
120             }
121             if(forMax > max){
122                 max = forMax;
123             }
124         });
125         return max;
126     }
127     setList=():void=>{
128         //清零
129         this.state.list.length = 0;
130         const list = [...this.state.cacheList];
131         this.setState({list,eventIF:true});
132         //解除绑定
133         if(this.props.val != undefined && this.props.val.domId != undefined){
134             let dom:any = document.getElementById(this.props.val.domId);
135             let event:string = "click";
136             if(this.props.val.event === EVENT.MOUSEENTER){
137                 event = "mouseenter";
138             }
139             dom.removeEventListener(event,this.setList);        
140         }
141     }
142     bindEvent=(val:any):void=>{
143         if(this.props.val != undefined && this.props.val.domId != undefined && this.props.val.event != undefined){
144             const dom:any = document.getElementById(this.props.val.domId);
145             let event:string = "click";
146             if(this.props.val.event === EVENT.MOUSEENTER){
147                 event = "mouseenter";
148             }
149             dom.addEventListener(event,this.setList);                
150         }
151     }
152     textReturn = (data:Array<Array<any>>,width:number):any =>{
153         if(this.props.text !== undefined){
154             const typesetting:string = this.props.text.typesetting !== undefined ? this.props.text.typesetting : 'vertical';
155             const wid:number = this.props.text.interval !== undefined ? this.props.text.interval : width;
156             return data[0].map((data)=>{return <Texts data={data} typesetting={typesetting} interval={wid}/>});
157         }else{
158             return undefined;
159         }
160     }
161     render() { 
162         //默认动画效果
163         const defVal:valFrom = {
164             type:TYPE.FADEIN,
165             direction:DIRECTION.LEFT,
166             time:.5,
167             sonDelay:.1,
168             delay:0,
169         };
170         const defV = {...defVal,...this.props.val}
171         let styleSon:any | undefined = this.props.styleSon;
172         if(this.props.val !== undefined && this.props.val.typesetting !== undefined && this.props.val.typesetting =='transverse'){
173             if(this.props.styleSon !== undefined){
174                 styleSon = {...this.props.styleSon,float:'left'}
175             }else{
176                 styleSon = {float:'left'}
177             }
178         }
179         //Son项数
180         let index:number = 0;
181         //最大文字占格
182         let width:number=0;
183         //字体大小
184         let fontSize:number = 13;
185         //Son高度
186         let formatHeight:number = 26;
187         //Father及Son宽度
188         let formatWidth:number = 0;
189        
190         let sonStr:any = this.props.children;
191         //宽高自适应
192         if(this.props.children != undefined){
193             width = this.isWidth(sonStr);
194         }
195         if(this.props.dataSource != undefined){
196             width = this.isWidth1(width,this.props.dataSource);
197         }
198         fontSize = this.props.style!==undefined && this.props.style.fontSize!==undefined?Number.parseInt(this.props.style.fontSize):13;
199         formatHeight = fontSize*2;
200         //调整特效字体默认宽度
201         if(width === 0){
202             width = 5;
203         }
204         formatWidth = fontSize*width*0.6;
205         //绑定dom后是否隐藏模板
206         let hideModel = "visible";
207         if(!this.state.eventIF){
208             //清零
209             this.state.list.length = 0;
210             this.state.cacheList.length = 0;
211             //子项写入
212             //为文字特效
213             if(this.props.text !== undefined && this.props.text.textEffect){
214                 const separate:string = this.props.text.separate !== undefined ? this.props.text.separate : ' ';
215                 const dataSource:Array<string> = this.props.text.testDataSource.split(separate);
216                 let newData:Array<any> = new Array();
217                 let ind:number = 0;
218                 for(let i=0;i<dataSource.length;i++){
219                     let data:Array<any> = new Array();
220                     for(let j=0;j<dataSource[i].length;j++){
221                         //若存在行延时
222                         if(this.props.text.rowDelay !== undefined){
223                             data.push(<List title={dataSource[i].substring(j,j+1)} index={ind++}
224                                 styleSon={styleSon} animation={defV} formatHeight={formatHeight} formatWidth = {formatWidth} 
225                                 keys={Number.MAX_VALUE-i-j} rowDelay={this.props.text.rowDelay*i}/>);
226                         }else{
227                             data.push(<List title={dataSource[i].substring(j,j+1)} index={ind++}
228                                 styleSon={styleSon} animation={defV} formatHeight={formatHeight} formatWidth = {formatWidth} 
229                                 keys={Number.MAX_VALUE-i-j}/>);
230                         }
231                         
232                     }
233                     newData.push(data);
234                 }
235             this.state.cacheList.push(newData);
236             }else{
237                 //为普通排版
238                 //Son子项追加
239                 if(this.props.children != null && this.props.children != undefined){  
240                     for(let i=0;i<sonStr.length;i++){
241                         if(sonStr[i].type!==undefined && sonStr[i].type===Son){
242                             this.state.cacheList.push(<List title={sonStr[i].props.children} style={sonStr[i].props.style} styleSon={this.props.styleSon}
243                             animation={defV} index={index++} formatHeight={formatHeight}
244                             formatWidth = {formatWidth} keys={this.props.children[i].props.keys !==undefined?
245                             this.props.children[i].props.keys:Number.MAX_VALUE-i} onClick={this.props.children[i].props.onClick}
246                             onClickSon={this.props.onClickSon} onMouseEnter={this.props.children[i].props.onMouseEnter}
247                             onMouseEnterSon={this.props.onMouseEnterSon} onMouseLeave={this.props.children[i].props.onMouseLeave}
248                             onMouseLeaveSon={this.props.onMouseLeaveSon}/>);
249                         }
250                     }
251                 }
252                 //dataSource追加
253                 if(this.props.dataSource !== undefined){
254                     for(let i=0;i<this.props.dataSource.length;i++){
255                         this.state.cacheList.push(<List title={this.props.dataSource[i].title} style={this.props.dataSource[i].style} index={index++}
256                         styleSon={styleSon} animation={defV} formatHeight={formatHeight} formatWidth = {formatWidth} keys=
257                         {this.props.dataSource[i].keys}/>);
258                     }
259                 }
260             }
261             //无dom绑定
262             if(defV.domId ===undefined || defV.event ===undefined){
263                 for(let i =0;i<this.state.cacheList.length;i++){
264                     this.state.list.push(this.state.cacheList[i]);
265                 }
266                 
267             }else{
268                 //有dom绑定
269                 if(this.props.val!=undefined && this.props.val.hideModel){
270                     hideModel = "hidden";
271                 }
272                 //事件绑定
273                 const _this  = this;
274                 //切换菜单后window.onload不会执行,但dom已经重置
275                 if(this.props.val != undefined && this.props.val.domId != undefined && this.props.val.event != undefined && 
276                 document.getElementById(this.props.val.domId)==null){
277                     let interval = window.setInterval(()=>{
278                         let dom:any = null;
279                         if(_this.props.val!=undefined && _this.props.val.domId != undefined){
280                             dom = document.getElementById(_this.props.val.domId);
281                         }
282                         if(dom !== null && dom !==undefined && dom !=="null"){
283                             _this.bindEvent(defV);
284 
285                             window.clearInterval(interval); 
286                         } 
287                     }, 100);        
288                 }
289             }
290         }else {
291             index = this.state.list.length;
292         }
293         
294         //Father默认样式
295 
296         const defFatherStyle:any = {
297             //border:"1px solid #91D5FF",
298             //backgroundColor: "#E6F7FF",
299             fontSize:"13px",
300             color:"#000",
301             paddimg:`${fontSize}px`,
302             height: `${formatHeight*index+2}px`,
303             width:`${formatWidth+2}px`,
304             visibility:`${hideModel}`,
305         }
306         const style = {...defFatherStyle,...this.props.style};
307         return (
308             <Fragment>
309                 {
310                     this.props.text !== undefined && this.props.text.textEffect ?
311                     this.textReturn(this.state.list,formatWidth)
312                     :
313                     (
314                         <div style={style} className={styles.fDiv}>
315                             <div className={styles.ul}>
316                                 {this.state.list}
317                             </div>
318                         </div>
319                     )
320                 }
321             </Fragment>
322          );
323     }
324 }
325 interface TestsProps{
326     data:any,//一行或一列数据
327     typesetting:string,//排版方法
328     interval:number,//宽或高
329 }
330 class Texts extends React.Component<TestsProps, {}>{
331     render(){
332         return(
333             this.props.typesetting === 'vertical' ?
334             (
335                 <div style={{float:"left",width:`${this.props.interval}px`}}>
336                     {this.props.data}
337                 </div>
338             ):(
339                 <div style={{height:`${this.props.interval}px`}}>
340                     {this.props.data}
341                 </div>
342             )
343         )
344     }
345 }
346 interface SonProps{
347     style?:any,//样式
348     keys?:any,//单个元素的key
349     onClick?:any,//鼠标点击事件
350     onMouseEnter?:any,//鼠标移入事件
351     onMouseLeave?:any//鼠标移出事件
352 }
353 export class Son extends React.Component<SonProps, {}> {
354 }
355 interface ListProps{
356     title:string,//文本
357     style?:any,//样式
358     styleSon?:any,//样式,渲染等级低于style
359     animation:valFrom,//动画样式
360     keys:any,//单个元素key
361     index:number,//当前元素排名,用于延时渲染
362     formatHeight:number,//单个元素高
363     formatWidth:number,//单个元素宽
364     onClick?:any,//鼠标点击事件,用于Son
365     onClickSon?:any,//鼠标点击事件,用于Father
366     onMouseEnter?:any,//鼠标移入事件,用于Son
367     onMouseEnterSon?:any,//鼠标移入事件,用于Father
368     onMouseLeave?:any,//鼠标移出事件,用于Son
369     onMouseLeaveSon?:any,//鼠标移出事件,用于Father
370     rowDelay?:number//行延时
371 }
372 class List extends React.Component<ListProps,{}> {
373     click = (key:any,title:any)=>{
374         if(this.props.onClick !== undefined){
375             this.props.onClick(key,title);
376         }else if(this.props.onClickSon !== undefined){
377             this.props.onClickSon(key,title);
378         }
379     }
380     mouseEnter = (key:any,title:any)=>{
381         if(this.props.onMouseEnter !== undefined){
382             this.props.onMouseEnter(key,title);
383         }else if(this.props.onMouseEnterSon !== undefined){
384             this.props.onMouseEnterSon(key,title);
385         }
386     }
387     mouseLeave = (key:any,title:any)=>{
388         if(this.props.onMouseLeave !== undefined){
389             this.props.onMouseLeave(key,title);
390         }else if(this.props.onMouseLeaveSon !== undefined){
391             this.props.onMouseLeaveSon(key,title);
392         }
393     }
394 
395 
396 
397     render() {
398         const val:valFrom = this.props.animation;
399         const style = {animation:'',animationDelay:'0s',animationTimingFunction:'ease-out'};
400         //加载页面后直接执行
401         if(val.type === TYPE.FADEIN && val.direction === DIRECTION.TOP || val.type === 'fadeIn' && val.direction === 'top'
402         || val.type === TYPE.FADEIN && val.direction === 'top' || val.type === 'fadeIn' && val.direction === DIRECTION.TOP){
403             style.animation= `${styles.fadeInTop} ${val.time}s forwards`;
404         }else if(val.type === TYPE.FADEIN && val.direction === DIRECTION.BUTTOM || val.type === 'fadeIn' && val.direction === 'buttom'
405         || val.type === TYPE.FADEIN && val.direction === 'buttom' || val.type === 'fadeIn' && val.direction === DIRECTION.BUTTOM){
406             style.animation = `${styles.fadeInButtom} ${val.time}s forwards`;       
407         }else if(val.type === TYPE.FADEIN && val.direction === DIRECTION.LEFT || val.type === 'fadeIn' && val.direction === 'left'
408         || val.type === TYPE.FADEIN && val.direction === 'left' || val.type === 'fadeIn' && val.direction === DIRECTION.LEFT){
409         style.animation = `${styles.fadeInLeft} ${val.time}s forwards`;
410         }else if(val.type === TYPE.FADEIN && val.direction === DIRECTION.REGIST || val.type === 'fadeIn' && val.direction === 'regist'
411         || val.type === TYPE.FADEIN && val.direction === 'regist' || val.type === 'fadeIn' && val.direction === DIRECTION.REGIST){
412             style.animation = `${styles.fadeInRegist} ${val.time}s forwards`;
413         }else if(val.type === TYPE.FADEIN && val.direction === DIRECTION.TOPLEFT || val.type === 'fadeIn' && val.direction === 'topLeft'
414         || val.type === TYPE.FADEIN && val.direction === 'topLeft' || val.type === 'fadeIn' && val.direction === DIRECTION.TOPLEFT){
415             style.animation = `${styles.fadeInTopLeft} ${val.time}s forwards`;
416         }else if(val.type === TYPE.FADEIN && val.direction === DIRECTION.TOPREGIST || val.type === 'fadeIn' && val.direction === 'topRegist'
417         || val.type === TYPE.FADEIN && val.direction === 'topRegist' || val.type === 'fadeIn' && val.direction === DIRECTION.TOPREGIST){
418             style.animation = `${styles.fadeInTopRegist} ${val.time}s forwards`;
419         }else if(val.type === TYPE.FADEIN && val.direction === DIRECTION.BUTTOMLEFT || val.type === 'fadeIn' && val.direction === 'buttomLeft'
420         || val.type === TYPE.FADEIN && val.direction === 'buttomLeft' || val.type === 'fadeIn' && val.direction === DIRECTION.BUTTOMLEFT){
421             style.animation = `${styles.fadeInButtomLeft} ${val.time}s forwards`;
422         }else if(val.type === TYPE.FADEIN && val.direction === DIRECTION.BUTTOMREGIST || val.type === 'fadeIn' && val.direction === 'buttomRegist'
423         || val.type === TYPE.FADEIN && val.direction === 'buttomRegist' || val.type === 'fadeIn' && val.direction === DIRECTION.BUTTOMREGIST){
424             style.animation = `${styles.fadeInButtomRegist} ${val.time}s forwards`;
425         }
426         if(val.sonDelay !== undefined && val.delay !== undefined){
427             if(this.props.rowDelay !== undefined){
428                 style.animationDelay = `${this.props.index*val.sonDelay+val.delay+this.props.rowDelay}s`;
429             }else{
430                 style.animationDelay = `${this.props.index*val.sonDelay+val.delay}s`;
431             }
432             
433         }
434         //Son默认样式
435         const defStyle:any = {
436             textAlign: "center",
437             width:`${this.props.formatWidth}px`,
438             height:`${this.props.formatHeight}px`,
439             lineHeight:`${this.props.formatHeight}px`,
440         }
441         const sty = {...defStyle,...this.props.styleSon,...this.props.style,...style};
442         return ( 
443             <li className={styles.li} style={sty} key={this.props.keys} onClick={this.click.bind(this,this.props.keys,this.props.title)}
444             onMouseEnter = {this.mouseEnter.bind(this,this.props.keys,this.props.title)} onMouseLeave=
445             {this.mouseLeave.bind(this,this.props.keys,this.props.title)}>{this.props.title}</li>
446          );
447     }
448 }

 

index.less文件:

  1 @top:50px;
  2 @left:50px;
  3 .fDiv,.li,.ul,body,div{
  4     padding: 0px;
  5     margin: 0px;
  6     border: 0px;
  7 }
  8 .fDiv{
  9     position: relative;
 10 }
 11 .li{
 12     list-style:none;
 13     visibility:hidden;
 14     cursor: pointer; 
 15     font:12px/1.5 Tahoma,Helvetica,Arial,'宋体',sans-serif;
 16 }
 17 
 18 // .li:hover{
 19 //     background-color: #A1E5FF;
 20 //     text-decoration:underline;
 21 // }
 22 // .ul{
 23 //     position: absolute;
 24 //     z-index: 999;
 25 //     display: inline-block;
 26 // }
 27 @keyframes fadeInTop{
 28     0%{
 29         opacity: 0;
 30         margin-top: @top;
 31         visibility:visible;
 32     }
 33     100%{
 34         opacity: 1;
 35         margin-top: 0px;
 36         visibility:visible;
 37     }
 38 }
 39 @keyframes fadeInButtom{
 40     0%{
 41         opacity: 0;
 42         margin-top: -@top;
 43         visibility:visible;
 44     }
 45     100%{
 46         opacity: 1;
 47         margin-top: 0px;
 48         visibility:visible;
 49     }
 50 }
 51 @keyframes fadeInLeft{
 52     0%{
 53         opacity: 0;
 54         margin-left: @left;
 55         visibility:visible;
 56     }
 57     100%{
 58         opacity: 1;
 59         margin-left: 0px;
 60         visibility:visible;
 61     }
 62 }
 63 @keyframes fadeInRegist{
 64     0%{
 65         opacity: 0;
 66         margin-left: -@left;
 67         visibility:visible;
 68     }
 69     100%{
 70         opacity: 1;
 71         margin-left: 0px;
 72         visibility:visible;
 73     }
 74 }
 75 @keyframes fadeInTopLeft{
 76     0%{
 77         opacity: 0;
 78         margin-top: @top;
 79         margin-left: @left;
 80         visibility:visible;
 81     }
 82     100%{
 83         opacity: 1;
 84         margin-top: 0px;
 85         margin-left: 0px;
 86         visibility:visible;
 87     }
 88 }
 89 @keyframes fadeInTopRegist{
 90     0%{
 91         opacity: 0;
 92         margin-top: @top;
 93         margin-left: -@left;
 94         visibility:visible;
 95     }
 96     100%{
 97         opacity: 1;
 98         margin-top: 0px;
 99         margin-left: 0px;
100         visibility:visible;
101     }
102 }
103 @keyframes fadeInButtomLeft{
104     0%{
105         opacity: 0;
106         margin-top: -@top;
107         margin-left: @left;
108         visibility:visible;
109     }
110     100%{
111         opacity: 1;
112         margin-top: 0px;
113         margin-left: 0px;
114         visibility:visible;
115     }
116 }
117 @keyframes fadeInButtomRegist{
118     0%{
119         opacity: 0;
120         margin-top: -@top;
121         margin-left: -@left;
122         visibility:visible;
123     }
124     100%{
125         opacity: 1;
126         margin-top: 0px;
127         margin-left: 0px;
128         visibility:visible;
129     }
130 }

 

API:

 

 代码示例:

 

 

 

 运行结果:

电脑端:

posted @ 2019-11-15 18:13  不老梦  阅读(268)  评论(0编辑  收藏  举报