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

Posted surfaces

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移动端 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 throttle1(fn, wait) {
    let previous = 0;
    return function () {
        let now = new Date().getTime();
        if (now - previous > wait) {
            fn.apply(this, arguments);
            previous = now;
        }
    }
}
					
	function raf_debounce(fn){ //touchmove  scroll节流
     var ticking=false;
     var that=this;
 
    return function(e) {
    
    	 var ev=e||window.event;
    	 //	console.log("正1确的e",e);
       if(!ticking) {
            ticking = true;
         requestAnimationFrame(function(){
         		//console.log("正2确的e",ev);
                 fn(ev);
                 ticking = false;
         });
       }  
     } 
};

var sb=1;
 window.addEventListener("scroll",function(e){
 	  if(sb){
 	  	sb=0;
 	  	  //var d = (+new Date());
 	  	console.log("sb",e)
 	  }
 })
var sa=1;
 window.addEventListener("scroll",throttle1(function(e){
 if(sa){
 	//sa=0;
   var d = (+new Date());
       console.log("sa",e);
   }
 },12))
 
 
 var sc=1;
 window.addEventListener("scroll",raf_debounce(function(e){
 if(sc){
 	//sa=0;
   var d = (+new Date());
       console.log("sc",e);
   }
 }))

  


 1513763671784
 1513763671800
 1513763671817
 1513763671834
 1513763671850
1513763671867
 1513763671883
 1513763671900
 1513763671917
1513763671934

观察最后2位 数字    后一个大于前一个的 16         两个大约相差16.6毫秒 执行scroll fn一次

注意正确的上下文 event 对象

 

 

   document.onmousemove = moveBy;
//                    document.onmousemove =   raf_debounce(function(e){
//                    	 moveBy (e);
//                    });

  

 

充分合理 使用 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)

以上是关于移动端 touchmove高频事件与requestAnimationFrame的结合优化的主要内容,如果未能解决你的问题,请参考以下文章

移动端与PC端的触屏事件

javascript--------------移动端事件-------------劉

移动端表层div滑动,导致底层body滑动(touchmove的阻止)

移动端 ios touchmove滑到边界(浏览器地址拦以以上) touchend失效解决办法

移动端web,tap与click事件

移动端touch事件和click事件的区别