一个原生JavaScript动画库原型

  • A+
所属分类:Web前端
摘要

设计目标:简单易用,不依赖其他库,对旧版浏览器具有一定兼容性,功能可扩展。

设计目标:简单易用,不依赖其他库,对旧版浏览器具有一定兼容性,功能可扩展。

动画调用:

 1 <!DOCTYPE html>  2 <html lang="en">  3 <head>  4     <meta charset="UTF-8">  5     <title>为若干个标签添加若干个动画,动画要有变化属性(默认为样式属性)、动画名、属性值类型、是否循环、多关键帧、ease函数(默认为线性)、有添加方法、删除方法</title>  6     <style>  7         .div_ani {background: red;width: 100px;height:100px;position:relative}  8     </style>  9     <script src="Ani.js"></script> 10 </head> 11 <body> 12     <div class="div_ani">111</div> 13     <div class="div_ani">222</div> 14 </body> 15 <!--script type="module"--> 16 <script> 17     //import Ani from './ani.js' 18     let objs1=document.querySelector(".div_ani"); 19     let objs2=document.querySelectorAll(".div_ani"); 20     Ani.addAni(objs2,[ 21     { 22       arr_pname:"width",loop:true,endcallback:function(){console.log("动画结束")}//动画属性 23       ,arr_key:[//关键帧数组 24         {ms:0,value:100,callback:function(ani){console.log(ani.elem.innerHTML+"第0ms")}}, 25         {ms:1000,value:300,callback:function(){console.log("第1000ms")}}, 26         {ms:2000,value:100,callback:function(){console.log("第2000ms")}}, 27       ] 28     } 29   ]//addAni的第一个参数可以是dom对象或dom对象数组,第二个参数是动画对象数组 30     ) 31   //可以多次执行addAni添加更多动画 32     Ani.startAni();//启动所有动画 33      34  35  36  37      38 </script> 39 </html>

 

代码实现:

  1 //考虑到更好的兼容性   2 // export default class Ani{   3 //     constructor(elem,p){   4 //         /*let p_default={   5 //             type:"css_px",   6 //             id:Ani.randomString(32),//没有必要生成两次   7 //   8 //         }*/   9 //         this.type=p.type||"css_px";  10 //         this.id=p.id||Ani.randomString(32);  11 //         this.arr_key=[];  12 //         let len=p.arr_key.length;//每个基础类型属性都要用直接量赋值  13 //         for(let i=0;i<len;i++)  14 //         {  15 //             let key=p.arr_key[i]  16 //             this.arr_key.push(  17 //                 {  18 //                     ms:key.ms,  19 //                     value:JSON.parse(JSON.stringify(key.value)),  20 //                     callback:key.callback,//回调函数可以共用一个  21 //                 }  22 //             )  23 //         }  24 //         //this.arr_key=p.arr_key;  25 //         this.loop=p.loop;  26 //         this.elem=elem;  27 //         this.countms=0;  28 //         this.arr_pname=p.arr_pname;  29 //         this.func_ease=p.func_ease||Ani.obj_func_ease.float_line;  30 //         //this.arr_framecallback=p.arr_framecallback||[];  31 //         this.endcallback=p.endcallback;  32 //         this.func_set=p.func_set||Ani.setValue;  33 //     }  34 // }  35 function Ani(elem,p){  36     this.type=p.type||"css_px";  37     this.id=p.id||Ani.randomString(32);  38     this.arr_key=[];  39     var len=p.arr_key.length;//每个基础类型属性都要用直接量赋值  40     for(var i=0;i<len;i++)  41     {  42         var key=p.arr_key[i]  43         this.arr_key.push(  44             {  45                 ms:key.ms,  46                 value:JSON.parse(JSON.stringify(key.value)),  47                 callback:key.callback,//回调函数可以共用一个  48             }  49         )  50     }  51     //this.arr_key=p.arr_key;  52     this.loop=p.loop;  53     this.elem=elem;  54     this.countms=0;  55     this.arr_pname=p.arr_pname;  56     this.func_ease=p.func_ease||Ani.obj_func_ease.float_line;  57     //this.arr_framecallback=p.arr_framecallback||[];  58     this.endcallback=p.endcallback;  59     this.func_set=p.func_set||Ani.setValue;  60 }  61 Ani.obj_anis={};  62 Ani.addAni=function(objs,p_anis)//添加动画  63 {  64     if(objs.outerHTML)//如果是一个html标签  65     {  66         objs=[objs];  67     }  68     var len1=objs.length;  69     var len2=p_anis.length;  70     for(var i=0;i<len1;i++)  71     {  72         var obj=objs[i];  73         /*if(!obj.arr_ani)//如果以前没有动画数组,dom元素没有易于使用的唯一id,所以不应把动画配置保存再dom元素对象里  74         //使用一个全局obj_anis保存所有动画对象  75         {  76             obj.arr_ani=[];  77         }*/  78         for(var j=0;j<len2;j++)  79         {  80             var p_ani=p_anis[j];  81             var ani=new Ani(obj,p_ani);  82             Ani.obj_anis[ani.id]=ani;  83             //let len3=obj.arr_ani;  84             /*let flag_canpush=true;  85             for(let k=0;k<len3;k++)  86             {  87                 //if(obj.arr_ani[k]===ani)//如果已经添加过这个动画对象  88                 if(obj.arr_ani[k].id==p_ani.id)  89                 {  90                     flag_canpush=false;  91                 }  92             }  93             if(flag_canpush==true)  94             {  95                 obj.arr_ani.push(p_ani);  96             }*/  97         }  98   99     } 100 } 101 Ani.runningAni=false; 102  103 //这里的一个问题是dom元素可能没有唯一id,后面很难再找到它的对象-》使用一个公用对象? 104 Ani.startAni=function() 105 { 106     if(Ani.runningAni==false) 107     { 108         Ani.lastframe=new Date().getTime(); 109         //Ani.currentframe=Ani.lastframe; 110         window.requestAnimFrame(function(){ 111             Ani.Frame(); 112         }) 113     } 114     Ani.runningAni=true; 115 } 116 Ani.pauseAni=function() 117 { 118     Ani.runningAni=false; 119 } 120  121 Ani.Frame=function() 122 { 123     Ani.currentframe=new Date().getTime(); 124     Ani.framems=Ani.currentframe-Ani.lastframe; 125  126     //key_called=null; 127     for(var key in Ani.obj_anis) 128     { 129         var ani=Ani.obj_anis[key]; 130         ani.countms+=Ani.framems; 131         var len=ani.arr_key.length; 132         var value=null; 133         for(var i=0;i<len-1;i++) 134         { 135             var key1=ani.arr_key[i]; 136             var key2=ani.arr_key[i+1]; 137             if(ani.countms>=key1.ms&&ani.countms<=key2.ms) 138             { 139                 value=ani.func_ease(key1,key2,ani.countms); 140                 //Ani.setValue(ani.elem,value,ani.type,ani.arr_pname); 141                 ani.func_set(ani.elem,value,ani.type,ani.arr_pname); 142                 if(key1.callback&&!key1.called)//如果这个关键帧上有回调函数,并且这个帧没有被调用过 143                 { 144                     key1.callback(ani); 145                     key1.called=true;//多个动画时有多个key1,要分别标记 146                 } 147                 break; 148             } 149         } 150         var endKey=ani.arr_key[len-1]; 151         if(ani.countms>endKey.ms)//如果完成了一次动画 152         { 153             //Ani.setValue(ani.elem,endKey.value,ani.type,ani.arr_pname); 154             ani.func_set(ani.elem,value,ani.type,ani.arr_pname); 155             if(endKey.callback)//如果结束帧上有回调函数 156             { 157                 endKey.callback(); 158                 console.log(ani.countms); 159             } 160             if(ani.loop) 161             { 162                 ani.countms=ani.countms%endKey.ms; 163                 for(var i=0;i<len;i++) 164                 { 165                     var key1=ani.arr_key[i]; 166                     key1.called=false; 167                 } 168                  169             } 170             else{ 171                 if(ani.endcallback)//如果有动画结束回调 172                 { 173                     ani.endcallback(ani); 174                 } 175                  176                 delete Ani.obj_anis[key]; 177                 ani=null; 178             } 179         } 180  181     } 182      183  184     Ani.lastframe=Ani.currentframe; 185     if(Ani.runningAni==true) 186     { 187         window.requestAnimFrame(function(){ 188             Ani.Frame(); 189         }) 190     } 191 } 192  193  194 Ani.setValue=function(elem,value,type,arr_pname)//在这里扩展更多的属性设置方法 195 { 196     if(type=="css_px")//这时arr_pname只有一层,是一个字符串,处理颜色时则可能是3维数组 197     { 198         elem.style[arr_pname]=value+"px"; 199     } 200     else if(type=="transform-rotate-deg")// 201     { 202         elem.style["transform"]="rotate("+value+"deg)"; 203     } 204 } 205 Ani.obj_func_ease={//在这里扩展更多的插值方法 206     //单浮点数线性 207     float_line:function(key1,key2,countms){ 208         var ms1=key1.ms; 209         var ms2=key2.ms; 210         var v1=key1.value; 211         var v2=key2.value 212         return Ani.inBetween(ms1,ms2,v1,v2,countms); 213     } 214 }; 215 //插值 216 Ani.inBetween=function(ms1,ms2,v1,v2,ms) 217 { 218     var v=v1+((ms-ms1)/(ms2-ms1))*(v2-v1); 219     return v; 220 } 221 Ani.randomString=function(len) 222 { 223     len = len || 32; 224     var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';    /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/ 225     var maxPos = $chars.length; 226     var pwd = ''; 227     for (var i = 0; i < len; i++) { 228         pwd += $chars.charAt(Math.floor(Math.random() * maxPos)); 229     } 230     return pwd; 231 } 232  233 // Copyright 2010, Google Inc. 234     window.requestAnimFrame = (function() { 235         return window.requestAnimationFrame || 236             window.webkitRequestAnimationFrame || 237             window.mozRequestAnimationFrame || 238             window.oRequestAnimationFrame || 239             window.msRequestAnimationFrame || 240             function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { 241                 window.setTimeout(callback, 1000/60); 242             }; 243     })();

编写过程中参考了Babylon.js动画模块、Google requestAnimFrame方法、网上的randomString方法。可基于MIT协议发布的部分基于MIT协议发布,项目Github地址:https://github.com/ljzc002/SimpleAni。