移动端 touchmove高频事件与requestAnimationFrame的结合优化

移动端最高频耗内存的的操作  莫属 touchmove 与scroll事件  两者需要 微观的 优化,使用 requestAnimationFrame性能优化 H5性能优化requestAnimationFrame

 这里 我们 讲述 touchmove;touchmove 事件发生很频繁,会比屏幕刷新率快,导致无效的渲染和重绘;

帧数 –显示设备通常的刷新率通常是50~60Hz –1000ms / 60 ≈ 16.6ms(1毫秒的优化意味着 6%的性能提升)

这就是 常说的  16.6毫秒的优化

浏览器对每一帧画面的渲染工作需要在16毫秒(1秒 / 60 = 16.66毫秒)之内完成 ;如果超过了这个时间限度,页面的渲染就会出现卡顿效果,也就是常说的jank;我们需要在  正确的时间  做正确的渲染;

 

拖拽的都会写,先上图看看效果;

大概了解一下 Timeline 查看看渲染情况   旧版如下(  新的  在  Performance )

 

ps   Performance 可以在控制台出入 查看

(function() {
 
    handleAddListener('load', getTiming)
 
    function handleAddListener(type, fn) {
        if(window.addEventListener) {
            window.addEventListener(type, fn)
        } else {
            window.attachEvent('on' + type, fn)
        }
    }
 
    function getTiming() {
        try {
            var time = performance.timing;
            var timingObj = {};
 
            var loadTime = (time.loadEventEnd - time.loadEventStart) / 1000;
 
            if(loadTime < 0) {
                setTimeout(function() {
                    getTiming();
                }, 200);
                return;
            }
 
            timingObj['重定向时间'] = (time.redirectEnd - time.redirectStart) / 1000;
            timingObj['DNS解析时间'] = (time.domainLookupEnd - time.domainLookupStart) / 1000;
            timingObj['TCP完成握手时间'] = (time.connectEnd - time.connectStart) / 1000;
            timingObj['HTTP请求响应完成时间'] = (time.responseEnd - time.requestStart) / 1000;
            timingObj['白屏时间'] = (time.responseStart - time.navigationStart) / 1000;

            timingObj['DOM渲染时间'] = (time.domComplete - time.domInteractive) / 1000;
            
            
            timingObj['domready时间--DOMContentLoaded事件完成的时间'] = (time.domContentLoadedEventEnd - time.fetchStart) / 1000;
            timingObj['onload时间 --页面所有资源完全加载的时间 '] = (time.loadEventEnd-time.fetchStart)/1000;
 
            for(item in timingObj) {
                console.log(item + ":" + timingObj[item] + '毫秒(ms)');
            }
 
            console.log(performance.timing);
 
        } catch(e) {
            console.log(timingObj)
            console.log(performance.timing);
        }
    }
})();

  

上面的代码 可以放入页面查看性能。

 

在看看 帧模式 渲染情况;

那些没必要的 move 什么也不需要做;没必要在16.6毫秒内多余的event对象计算;

 

关于帧模式:

 

 

普通的拖拽

<script>

	
	 function getStyle(obj,attr){
        return  obj.currentStyle? obj.currentStyle[attr]: getComputedStyle(obj,false)[attr];    
   }
	
		    var oDiv = document.getElementById("oDiv");   //当前元素
			var direction="horizontal"; 
			
			     var disX=0;
        	     var disY=0;
             
                 var self = this;  //上下文
                 var downLeft=0;
                 var downTop=0;
                 var isDown = false;
                 var oDivWidth=parseInt(oDiv.offsetWidth);
                oDiv.onmousedown = function (e) {
                	var e=e||window.event;
                 //鼠标按下,计算当前元素距离可视区的距离
                     
                     downLeft= parseInt(getStyle(oDiv,'left'));;
                     downTop= parseInt(getStyle(oDiv,'top'));;
                     
                     disX = e.clientX ;
                     disY = e.clientY;
                     
                    console.log("开始位置",e.clientX,"downLeft",downLeft);
                     isDown = true;
                    document.onmousemove = function (e) {
                    	var e=e||window.event;
                    	e.preventDefault();
                    	oDiv.style.cursor="move";
                    	
                    	 if (isDown == false) {
						        return;
						  }
                    	
                      //通过事件委托,计算移动的距离 
                        var l = e.clientX - disX+downLeft;
                        var t = e.clientY - disY+downTop;
                        
                      

                        
                        
                        
                      //移动当前元素  
                       if(direction=="horizontal"){//水品
                       	 oDiv.style.left = l + 'px';
                       }else if(direction=="vertical"){//垂直
                       	 oDiv.style.top = t + 'px';
                       }else{
                        oDiv.style.left = l + 'px';
                        oDiv.style.top = t + 'px';
                       }
                        
                        
          
                        
                        
                        
                        // console.log("移动位置",e.clientX,"移动中left",l,"最终",getOffset(oDiv).left);
                         //将此时的位置传出去
                        //binding.value({x:l,y:t,direction:direction})
                    };
                    document.onmouseup = function (e) {
                         var e=e||window.event;
                         
                      
                         
                         
                         var left2=e.clientX-disX;
                         var top2=e.clientY-disY;
                             isDown = false;
                        // console.log("结束位2置",e.pageX,"移asa中left",left2,"最终",getOffset(oDiv).left);
                         //将此时的位置传出去
                     
                        document.onmousemove = null;
                        document.onmouseup = null;
                         return false;    //FF等高版本浏览器中阻止默认行为
                     };
                };
           
        
			
		</script>

  

 

附上源代码:

 1 function drag(element){  
 2         
 3         var startX=0,
 4             startY=0,
 5             ticking=false,
 6             raf,
 7             doc=document;
 8             
 9         element.addEventListener("touchstart",function(e){
10              
11             
12                   var e=e||window.event,
13                    touchs = e.touches[0];
14                   e.preventDefault();       //低端安卓 touch事件 有的导致touchend事件时效  必须开始 就加   e.preventDefault();
15                                            // text a ipnut textarea 几个 等标签除外   
16                                             // ,另外自定义移动端touchstart touchend组合的 hover事件,建议不加这个,不然页面无法滚动
17                                             //touchmove 开始 就加  不然抖动一下,才能touchmove, 然后才正常 尤其早些的 三星   系列自带浏览器
18                                               
19                
20                    startX=parseInt(touchs.pageX-(element.lefts||0));
21                    startY=parseInt(touchs.pageY-(element.tops||0));
22                   
23                    doc.addEventListener("touchmove",update,false);
24                    doc.addEventListener("touchend",end,false);
25                             
26         },false);
27         
28         
29         
30         
31         
32         var  update=function (e) {
33              
34                var e=e||window.event;
35                if (e.touches.length > 1 || e.scale && e.scale !== 1) return;
36                 e.preventDefault();
37                
38                //cancelAnimationFrame(raf);
39                if(!ticking) {
40 
41                var touchs = e.changedTouches[0];
42                    
43                  //1先触摸移动  
44                     element.lefts = touchs.pageX - startX;
45                     element.tops = touchs.pageY - startY;
46                    
47                  //2交给requestAnimationFrame 更新位置
48                 //raf=requestAnimationFrame(function(){draw();});
49                  raf=requestAnimationFrame(draw);
50 
51                 }
52               
53                  ticking = true;
54            };
55         
56          
57          
58          
59          var draw= function  (){       
60                   ticking = false;
61                   var nowLeft=parseInt(element.lefts);    //滑动的距离             touchmove时候,如果加阻力,可能有细小的抖动;我想应该是移动端 部分支持0.5px的缘故; parseInt的转化有点牵强;
62                   var  nowTop=parseInt (element.tops);    //滑动的距离    
63                      
64                     element.style.webkitTransform=element.style.transform = "translate3D(" + nowLeft + "px," + nowTop + "px,0px)";
65                     
66               };
67                    
68         var end=function(){
69                      var endLeft= parseInt(element.lefts);    //滑动的距离    
70                      var  endTop= parseInt(element.tops);    //滑动的距离
71                     
72                      //element.style.webkitTransform=element.style.transform = "translate(" + endLeft+ "px," + endTop + "px)"; 
73     
74                         doc.removeEventListener("touchmove",update,false);
75                         doc.removeEventListener("touchend",end,false);
76                         // cancelAnimationFrame(raf);
77                          
78             }
79                 
80      };
81      

注意点:RequestAnimationFrame的兼容

;(function(){var lastTime=0;var vendors=["ms","moz","webkit","o"];for(var x=0;x<vendors.length&&!window.requestAnimationFrame;++x){window.requestAnimationFrame=window[vendors[x]+"RequestAnimationFrame"];window.cancelAnimationFrame=window[vendors[x]+"CancelAnimationFrame"]||window[vendors[x]+"CancelRequestAnimationFrame"]}if(!window.requestAnimationFrame){window.requestAnimationFrame=function(callback,element){var currTime=new Date().getTime();var timeToCall=Math.max(0,16-(currTime-lastTime));var id=window.setTimeout(function(){callback(currTime+timeToCall)},timeToCall);lastTime=currTime+timeToCall;return id}}if(!window.cancelAnimationFrame){window.cancelAnimationFrame=function(id){clearTimeout(id)}}}());

RequestAnimationFrame的简易动画库

 requestAnimationFrame 节流函数 

  function throttle(fn, wait) {
                let previous = 0;
                return function() {
                    let now = new Date().getTime();
                    if (now - previous > wait) {
                        fn.apply(this, arguments);
                        previous = now;
                    }
                }
            }
 
            function debounce(func, wait, immediate) {
                var wait = wait || 800;
                var timeout;
 
                return function() {
                    var context = this,
                        args = arguments;
                    var later = function() {
                        timeout = null;
                        if (!immediate) func.apply(context, args);
                    };
                    var callNow = immediate && !timeout;
                    clearTimeout(timeout);
                    timeout = setTimeout(later, wait);
                    // console.log('混入的钩子函数2');
                    if (callNow) func.apply(context, args);
                };
            }
 
 
            function raf_throttle(fn) { //
                var ticking = false;
 
 
                return function() {
                    var context = this,
                        args = arguments;
                    //console.log("正1确的e",args);
                    if (!ticking) {
                        ticking = true;
                        requestAnimationFrame(function() {
                           // console.log("正2确的e",ev);
                            fn.apply(context, args)
                            ticking = false;
//                          setTimeout(function(){
//                          	  ticking = false;
//                          },1500)
                        });
                    }
                }
            };
 
            function raf_debounce(fn, immediate) { //
 
                var timeout;
 
                return function() {
                    var context = this,
                        args = arguments;
                    var later = function() {
                        timeout = null;
                        if (!immediate) fn.apply(context, args);
                    };
                    var callNow = immediate && !timeout;
                    cancelAnimationFrame(timeout);
                    timeout = requestAnimationFrame(later);
                    if (callNow) fn.apply(context, args);
                };
            };
            
            
            function microtask2(fn) {
               var called = false;
              return function () {
  
  	           var context = this,  args = arguments;
  	
			    if (called) {
			      return;
			    }
			    called = true;
			    //var flah=false;
			    if(Promise){
			    Promise.resolve().then(function () {
			     called = false;
			      fn.apply(context, args)
			    });
			    }else{
			        setTimeout(function(){
			    	   	   called = false;
			       },0)
			      fn.apply(context, args)
			    }
			    
			  };
			}
            // vue 使用
            //import  throttle  from '@/utils/throttle.js';
            //import  debounce  from '@/utils/debounce.js';
            // methods: {
            //  throttle3:throttle(function(func,wait){
            //      //console.log('混入的throttle3',this.mixin_msg);
            //      func();
            //  },1000),
 
            //  debounce3:debounce(function(func,wait){
            //  //console.log('混入的debounce3',this.mixin_msg);
            //   func();
            //  },1000,true),
            // }
 
            //使用方法 1 -------------------------------
            // window.addEventListener("scroll", throttle(function(e) {
            //  console.time('使用方法 1');
            // }))
       
 
            //使用方法 2   -------------------------------
            var pre = 0
 
            function action5(e, sa) {
                //console.time('raf_debounce runTime1:');
                console.log("raf_debounce 动作停下后 16.6ms 执行最后一次 里面 e", e, "其他参数", sa)
                //console.timeEnd('raf_debounce runTime1:');
 
            }
 
 
            function action6(e, sa) {
                //console.time('raf_throttle runTime1:');
               var duction=+new Date-preStartStamp6;
            
                console.log("raf_throttle  动作持续 每隔16.6ms 执行一次 里面 duction", duction, "其他参数", sa)
               // console.timeEnd('raf_throttle runTime1:');
                 preStartStamp6=+new Date;
            }
 
            function action7(e, sa) {
               // console.time('throttle runTime2:');
                console.log("throttle 动作持续 每隔1s 执行一次 里面 e", e, "其他参数", -sa)
               // console.timeEnd('throttle runTime2:');
 
            }
 
            function action8(e, sa) {
                //console.time('debounce');
                console.log("debounce 动作停下 1s后执行 最后一次 里面 e", e, "其他参数", sa)
               // console.timeEnd('debounce')
 
            }
            
              function action9(e, sa) {
               // console.time('microtask2');
                   var duction=+new Date-preStartStamp9;
                  //console.log("action9 ",sa)          
                console.log("microtask2 动作持续 每隔16.6ms 执行一次 里面 duction", duction, "其他参数",sa)
               // console.timeEnd('microtask2')
                  preStartStamp9=+new Date;
            }
            var fn5 = raf_debounce(action5);
            var fn6 = raf_throttle(action6);
            var fn7 = throttle(action7, 1000);
            var fn8 = debounce(action8, 1000);
             var fn9 = microtask2(action9);//microtask2 优先与requestAnimationFrame 执行
            var  preStartStamp6=+new Date
             var  preStartStamp9=+new Date
            var abb = "外面参数数"
            window.addEventListener("scroll", function(e) {
            	
                var now = +new Date;
                //preStartStamp=now;
                var saa = abb+"-"+now
       
//              fn5(e, saa)
            // fn6(e, saa)
//              fn7(e, saa)
//              fn8(e, saa)
                fn9(e, saa)
                 
 
            })

		</script>

  



充分合理 使用 requestAnimationFrame性能优化 对H5网页的体验 有着微观细致的影响;

 

from memory cache与from disk cache

 

三级缓存原理

1、先查找内存,如果内存中存在,从内存中加载;
2、如果内存中未查找到,选择硬盘获取,如果硬盘中有,从硬盘中加载;
3、如果硬盘中未查找到,那就进行网络请求;
4、加载到的资源缓存到硬盘和内存;

三、HTTP状态码及区别
  • 200 form memory cache
    不访问服务器,一般已经加载过该资源且缓存在了内存当中,直接从内存中读取缓存。浏览器关闭后,数据将不存在(资源被释放掉了),再次打开相同的页面时,不会出现from memory cache。

  • 200 from disk cache
    不访问服务器,已经在之前的某个时间加载过该资源,直接从硬盘中读取缓存,关闭浏览器后,数据依然存在,此资源不会随着该页面的关闭而释放掉下次打开仍然会是from disk cache。

  • 304 Not Modified
    访问服务器,发现数据没有更新,服务器返回此状态码。然后从缓存中读取数据。


 


链接:https://www.jianshu.com/p/8332da83955d

参考网站:

谷歌开发者,非常专业:https://developers.google.com/web/fundamentals/getting-started/?hl=zh-cn 需要FQ;

Web前端性能优化的微观分析 http://velocity.oreilly.com.cn/2013/ppts/16_ms_optimization--web_front-end_performance_optimization.pdf

移动端性能调优:ttps://speakerdeck.com/baofen14787/yi-dong-duan-xing-neng-diao-you-ji-16msyou-hua

 

总结:做的东西多了,得 整理一下以;

移动端 scroll,touchmove的事件用的还是比较多的;有时间还是要 细细优化的;虽然感觉很微观 ,甚至觉得 优化的几乎看不出来;但是你去优化好,还是费不少时间 的;

requestAnimationFrame是个移动端的利器;动画尽量用它或者除集合css3实现;

 一个基于  requestAnimationFrame 的动画函数,仿造jquery  http://www.cnblogs.com/surfaces/p/5129868.html   

移动端 transition动画函数的封装(仿Zepto)以及 requestAnimationFrame动画函数封装(仿jQuery)

posted @ 2016-03-31 17:13  surfaces  阅读(4699)  评论(3编辑  收藏  举报