移动端 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的结合优化的主要内容,如果未能解决你的问题,请参考以下文章
javascript--------------移动端事件-------------劉
移动端表层div滑动,导致底层body滑动(touchmove的阻止)