zoukankan      html  css  js  c++  java
  • 移动端 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)

  • 相关阅读:
    如果前面的IO操作出问题了,按照我们代码的意思,不就try catch 了吗,这样的话线程就没关闭了,就会造成线程泄露。 那怎么解决这个问题呢,其实也简单,把关闭线程的方法写到finally里就可以了。
    Dataeye计算任务架构
    Mercury:唯品会全链路应用监控系统解决方案详解(含PPT)
    app 爬虫
    唯品会HDFS性能挑战和优化实践
    构建Hadoop监控共同体
    消除单点故障 flume
    时间戳 Flume's Memory Consumption
    telnet nmap netstap
    hdfs ha
  • 原文地址:https://www.cnblogs.com/surfaces/p/5341882.html
Copyright © 2011-2022 走看看