前言
前兩天去一家公司面試,被問到一些小游戲的東西。面試官提到了刷紅包還有抽獎這些怎么實現,當時簡單說了下思路,回來之后想想還是說的太輕描淡寫了,干說不做就是耍流氓,所以就做了一個(Demo & 源碼)。啟動方式:手指在轉盤上滑動,轉盤轉動。這里沒有像一般的抽獎程序一樣在后台指定抽獎結果,結果完全由你的手速決定的(老板哭了。。。)
界面
界面很簡單,網上搜個圖片或者直接搜個 demo 就有了,當然自適應也是必須的。這里用了 Rem 來實現自適應,所有尺寸單位均用 rem,改變 html 節點的 font-size 即可實現全屏縮放,這里設置的是當屏幕寬度小於420px的時候轉盤尺寸與屏寬城正比,當屏寬大於420px的時候轉盤尺寸固定。更多關於rem實現自適應的內容,可以看看這里: Here 。
動效與交互
網上看到的demo大多數是點擊啟動的,就想着換個交互方式,用觸屏滑動的方式啟動。這里很容易就能想到以滑屏速度轉動轉盤,轉動時給一個負加速度就可以實現減速了。這里要注意用戶體驗,為了讓人有一個順暢的感覺,啟動的速度必須要相當的快,結尾的時候要慢慢的減速,營造抽獎的緊張氣氛。所以加速度是有一個先大后小的變化,跟CSS中的 ease-out 一樣的效果。代碼如下:

var rotate = document.getElementById('imgs'); var speed = vspeed = 0, x0 = y0 = t0 = x1 = y1 = t1 = null; (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); }; }; })(); // Setup requestAnimationFrame when it is unavailable. document.addEventListener('touchmove', function (e) { e.preventDefault(); }); rotate.addEventListener('touchstart', function (e) { if (e.touches.length == 1) { x0 = e.targetTouches[0].clientX; y0 = e.targetTouches[0].clientY; t0 = new Date().getTime(); } }); rotate.addEventListener('touchend', function (e) { var that = this, l = 0, angle = 0, timerID = null; x1 = e.changedTouches[0].clientX; y1 = e.changedTouches[0].clientY; t1 = new Date().getTime(); l = Math.sqrt(Math.pow(x1-x0,2) + Math.pow(y1-y0,2)); speed = l/(t1-t0)*20; if (speed < 10) return; vspeed = 0.5; var roll = function () { angle += speed; that.style.transform = 'rotateZ(' + angle + 'deg)'; switch (true){ case speed < -0.3: window.cancelAnimationFrame(timerID); return; case speed < 10 && vspeed > 0.1: speed -= vspeed; vspeed -= 0.03; break; default: speed -= vspeed; break; } timerID = window.requestAnimationFrame(roll); }; roll(); });
這里動畫用的還是 h5 的 requestAnimationFrame 來實現,對於不支持 requestAnimationFrame 的瀏覽器也做了兼容。
編程還是相當簡單的,難點在於參數的調整,要調整速度控制整個轉動周期不能過長過短,而最麻煩的應該是最后減速階段的加速度調整了。最后階段太快了就沒有緊張的氣氛,太慢了又會有卡頓的感覺,尤其在國產移動瀏覽器上表現得更為明顯。
性能優化
在沒性能優化前,在國產的移動瀏覽器上卡頓得簡直瞎了眼,優化是必須的。
常規做法:分層,動態元素與靜態元素分離,也就是轉盤絕對定位、單獨占一個層,這樣的話動態元素的變化不會影響到靜態元素的布局,減少重繪。這個優化很基礎,寫頁面的時候就已經實施了,可見問題並不在此。另外之前一直聽說要用 CSS3d 變換的方法強制使用 GPU 渲染頁面提高性能,但是代碼上我已經寫了 'rotateZ()',應該已經開啟了硬件渲染了,不知道問題在哪,如何解決。空想也是無用,趕緊用 chrome developer 測試了一下:
可以看到數據其實還是相當不錯的,腳本、渲染和繪圖所占時間還比較合理的,但是問題會出在哪里呢?當然是國產瀏覽器身上了,那怎么去優化呢?再用 chrome 的渲染檢測工具看看吧。打開方式:
見名知義,第一項可以檢測頁面的繪制刷新(重繪),第二項顯示圖層邊界,第三項是顯示 FPS(Frame per Second,FPS,幀率),這幾項重點關注。因為這個是單頁應用,所以后面的滾動問題檢測和媒體仿真就略過了。啟動之后是這樣的:
最前面的綠色遮罩表示重繪區域,四周還有一圈褐色的表示渲染層,啟動動畫的時候可以發現在繪制動畫時事實上轉盤元素在不斷重繪,這應該是問題的關鍵。但怎么解決呢?很多文章在介紹使用 GPU 渲染時候都會提到下面這兩句樣式:
backface-visibility: hidden;
perspective: 1000;
試着用了這兩句之后,立馬出現了效果,旋轉的時候不再出現綠色的重繪框了:
再回到某國產移動瀏覽器上測試,終於沒有那種死活轉不起來的感覺了,感覺跟原生應用差不多了,其實原生應用無非也是用了 GPU 渲染而已,當 web 能調用這些底層的話,性能上差別不會很大。這里面其實后面一句 perspective: 1000; 是多余的,設這么大的透視距離目的是為了減弱 3D 效果,減少計算量,而轉盤轉動本來就沒 3D 效果,所以這里是沒效果的。
主要是 backface-visibility: hidden 起作用。backface 就是元素背面,元素和醫院照的X光片一樣,正反兩面都可以看。隱藏背面的意思就是轉過來是空白的,什么也沒有。如果不設置這個的話,瀏覽器會連物體的背面也渲染出來。由於轉盤元素上面還疊加有指針元素,如果不指定隱藏背面的話,那瀏覽器就將轉盤覆蓋的范圍全部重繪,比如說我繞 X 或者 Y 軸旋轉,既然畫出轉盤還要畫出指針,那肯定是要重繪這一大塊頁面的。至於隱藏背面之后為何不再發生渲染層的重繪,這個可能跟瀏覽器的渲染策略有關,這里瀏覽器應該會按照最優解去重繪所需的層,不變的層仍然保留,最后做合成算法。這一塊我也還不甚了解,這些只是我個人的理解,有不當之處請務必指正!
PS: 后續發現只要設置了 backface-visibility: hidden,根本不需要開啟 GPU,直接用 2D 旋轉也能得到非常好的效果!
鄧爺爺說得對,實踐是檢驗真理的唯一標准!
參考資料:
1)Accelerated Rendering in Chrome: http://www.html5rocks.com/en/tutorials/speed/layers/
2)App performance validation: https://developer.mozilla.org/en-US/Apps/Fundamentals/Performance/App_performance_validation
3)被解放的GPU CSS3動畫加速: http://www.cnblogs.com/sunshq/p/4878019.html
4)【Web動畫】CSS3 3D 行星運轉 && 瀏覽器渲染原理: http://www.cnblogs.com/coco1s/p/5439619.html#3420358
5)Web animations on large screens: https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox_OS_for_TV/Web_animations_on_large_screen
以上,碼字不易,隨手點贊哈
(圖片出處:小周)
原創文章,轉載請注明出處!本文鏈接:http://www.cnblogs.com/qieguo/p/5481522.html