前言
前兩天去一家公司面試,被問到一些小遊戲的東西。面試官提到了刷紅包還有抽獎這些怎麼實作,當時簡單說了下思路,回來之後想想還是說的太輕描淡寫了,幹說不做就是耍流氓,是以就做了一個(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();
});
View Code
這裡動畫用的還是 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