移動端 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 需要翻牆;

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)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM