手機互動網(wǎng)頁項目設(shè)計總結(jié)(下)
一、項目地址
1、TGA移動游戲官網(wǎng)
TGA城市拉力賽
2、UP+邀請函
3、天天酷跑里約進擊版
二、布局
1、按屏滾動會碰到什么問題?
問題:
按屏滾動即用戶滑動屏幕翻頁,屏幕自動鎖定到當(dāng)前頁。我們會碰到的問題:
1)、頁面100%高度包含地址欄高度,當(dāng)?shù)刂窓诖嬖跁r,會有部分內(nèi)容被隱藏,如下圖所示:
2)、如何阻止瀏覽器默認滾動事件更好?
解決:
1)、重置<html>高度;
document.documentElement.style.height = window.innerHeight + 'px';
2)、默認事件禁止touchmove比touchstart更好,否則還需要單獨處理<a><input>等問題。
document.documentElement.addEventListener('touchmove', function(e){ e.preventDefault(); });
2、flexbox會碰到什么問題?
問題:
自適應(yīng)高度/寬度的flex元素內(nèi),子元素尺寸以百分比為單位(如height:100%)無效。
解決:
需要將子元素設(shè)為絕對定位。
3、fixed定位需要慎用。
問題:
在ios系統(tǒng)的瀏覽器中,頁面加載時偶爾會出現(xiàn)抖動情況。
解決:
使用絕對定位或flexbox模擬解決。
三、動畫
1、CSS動畫與Canvas動畫性能優(yōu)劣分析
問題:
目前移動端動畫,特別在H5游戲與強交互性網(wǎng)站應(yīng)用非常普遍,常用的實現(xiàn)方式是CSS(硬件加速)與Canvas,但缺乏完整的性能分析數(shù)據(jù),我們在選擇動畫實現(xiàn)方式時還是存在糾結(jié)。
解決:
選用高中低不同配置不同系統(tǒng)的4款設(shè)備,進行了一輪測試:
1)、測試DEMO:
Canvas: 2個元素 | Canvas:10個元素 |
DOM:2個元素 | DOM:10個元素 |
2)、測試數(shù)據(jù):
紅色代表警告數(shù)據(jù),一般60以上較為順暢。
特征 測試機型 |
iPhone 4s (iOS6) |
iTouch 4 (iOS5) |
三星i9250 (Android 4.0) |
三星gt s6358 (Android 2.2) |
預(yù)計市場份額 | 中高端(>50%) | 中端(35%) | 低端(<15%) | |
Canvas: 2個元素 | 110 | 105 | 95(丟幀) | 20 |
Canvas:10個元素 | 110 | 65 | 65(丟幀) | 10 |
DOM:2個元素 | 115 | 80 | 100 | 12 |
DOM:10個元素 | 60 | 30 | 50 | 5 |
3)、測試結(jié)論:
a、CSS動畫更為流暢、但內(nèi)存占用過高,動畫元素在5個以內(nèi)更為推薦;
b、Canvas動畫存在丟幀現(xiàn)象,這一現(xiàn)象在android中低端手機中表現(xiàn)更為明顯;
c、5個以內(nèi)動畫元素,選用CSS動畫,80%的設(shè)備幀頻可達80以上。
2、當(dāng)縮放動畫碰上硬件加速
問題:
如下圖所示,如何平滑地實現(xiàn)底部導(dǎo)航的縮放呢?
1)、不能直接用scale來縮放,這樣會造成文字的縮放與虛化;
2)、不能通過 height的變化來更改高度,這樣不能啟用硬件加速。
解答:
需要分拆實現(xiàn),高度變化用translateY來做,單獨對里面的圖標使用scale。
代碼:
nav{transition:.5s all ;} nav .icon{transition:.5s all;transform-origin:center bottom;} nav.mini{transform: translate(0, 50px);} nav.mini .icon{transform: scale(0.5, 0.5);}
3、動畫由Javascript來維護還是css維護的思考
近期制作的幾個動畫較多的網(wǎng)站,在動畫的調(diào)整與維護上花了不少時間,于是有了以下思考。
1)、判斷優(yōu)劣維度
開發(fā)效率、維護、性能
2)、具體分析
a、由CSS維護。
實現(xiàn):
結(jié)合transition和animation,將動畫時間軸寫死在CSS中。
開發(fā)效率:高。
結(jié)合現(xiàn)有配套工具,能夠快速完成動畫制作。
維護:差。
如果動畫涉及回調(diào)函數(shù)執(zhí)行,時間軸同時存在于CSS與Javascript中,維護成本會大大提升。如果涉及判斷與循環(huán),動畫效果將同時由CSS與Javascript來維護,代價太高。
性能:高。
動畫無需解析Javascript,減少DOM操作,性能較Javascript的方式更高。
b、由Javascript維護。
封裝動畫框架,底層調(diào)用CSS動畫,時間軸由Javascript來維護。
開發(fā)效率:高。無需考慮各平臺兼容性,配置參數(shù)即可完成動畫,對于復(fù)雜動畫,也可開發(fā)配套工具。
維護:好。動畫效果、動畫邏輯,全部由Javascript來維護,動畫可控性更高,維護變得很輕松。
性能:較高。由于底層還是調(diào)用CSS動畫,DOM操作次數(shù)有限,性能損耗并不會太高。
3)、總結(jié)。
對于弱交互,無邏輯的動畫,可以選用CSS的方式。對于復(fù)雜動畫,更推薦由Javascript來維護。
四、特性
1、如何解決音頻播放預(yù)加載與延時的問題
問題:
通過對各個平臺下進行音頻特性測試,存在問題如下:
IOS6 | Android2.3 | Android4.0 | |
Audio標簽base64 | 不支持 | 不支持 | 不支持 |
預(yù)加載 | 不支持 | 支持 | 支持 |
自動播放 | 不支持 | 支持 | 支持 |
不同聲音同時播放 | 支持 | 支持 | 支持 |
播放延時 | 無 | 無 | 無 |
Web audio | 支持 | 不支持 | 不支持 |
解決:
問題/平臺 | IOS4-5 | IOS6-7 | Android2.3-4.2 |
預(yù)加載 | 不支持 |
Web audio (Base64-> ArrayBuffer) |
Audio對象 |
代碼:
封裝類:
var lib = { _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", decodeArrayBuffer: function(input) { var bytes = (input.length/4) * 3; var ab = new ArrayBuffer(bytes); this.decode(input, ab); return ab; }, decode: function(input, arrayBuffer) { var lkey1 = this._keyStr.indexOf(input.charAt(input.length-1)); var lkey2 = this._keyStr.indexOf(input.charAt(input.length-2)); var bytes = (input.length/4) * 3; if (lkey1 == 64) bytes--; if (lkey2 == 64) bytes--; var uarray; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; var j = 0; if (arrayBuffer) uarray = new Uint8Array(arrayBuffer); else uarray = new Uint8Array(bytes); input = input.replace(/[^A-Za-z0-9+/=]/g, ""); for (i=0; i<bytes; i+=3) { enc1 = this._keyStr.indexOf(input.charAt(j++)); enc2 = this._keyStr.indexOf(input.charAt(j++)); enc3 = this._keyStr.indexOf(input.charAt(j++)); enc4 = this._keyStr.indexOf(input.charAt(j++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; uarray[i] = chr1; if (enc3 != 64) uarray[i+1] = chr2; if (enc4 != 64) uarray[i+2] = chr3; } return uarray; }, getScript: function(url, callback, charset){ var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.src = url; charset && (script.charset = charset); script.onload = script.onreadystatechange = function() { if ((!this.readyState || this.readyState === "loaded" || this.readyState === "complete") ) { callback && callback(); script.onload = script.onreadystatechange = null; if (head && script.parentNode) head.removeChild(script); } }; head.appendChild(script); return script; } } var QAudio = function(config){ this.config = config; this.init(); } QAudio.prototype.init = function(){ var config = this.config; this.src = config.src; this.base64 = config.base64; if ('AudioContext' in window) { this.audioContext = new AudioContext(); } else if ('webkitAudioContext' in window) { this.audioContext = new webkitAudioContext(); } else { this.audio = document.createElement('audio'); this.audio.preload = 'auto'; this.audio.src = this.src; } // base64數(shù)據(jù)處理 if(this.audioContext) { this._handleBase64(); } this.load(); } QAudio.prototype._handleBase64 = function(){ var self = this; window[this.config.callback] = function(data){ var arrayBuff = lib.decodeArrayBuffer(data); self.audioContext.decodeAudioData(arrayBuff, function(audioData) { self._arrayBuff = audioData; }); } } QAudio.prototype.load = function(){ // 如果支持Web Audio,加載base64音頻 if(this.audioContext) { lib.getScript(this.base64); } else if(this.audio) { this.audio.load(); } } QAudio.prototype.play = function(){ if(this.audioContext) { var mySource = this.audioContext.createBufferSource(); mySource.buffer = this._arrayBuff; mySource.connect(this.audioContext.destination); if ('AudioContext' in window) { mySource.start(0); } else if ('webkitAudioContext' in window) { mySource.noteOn(0); } } else if(this.audio) { this.audio.play(); } }
調(diào)用:
new QAudio({src: './audio/pop1.mp3', base64: './audio/pop1.js', callback: 'cbPop1'});
示例:
2、重力感應(yīng)
問題:
重力感應(yīng)的實現(xiàn)相對簡單,具體見如下代碼。值得注意的是,android需3.0及以上才支持。
解決:
代碼:
var box = document.querySelector('.box'); var timer; window.addEventListener('deviceorientation', function(e) { var x = parseInt(e.gamma); var y = parseInt(e.beta); var r = parseInt(e.alpha); box.style.webkitTransform = 'translate3d(' + x*window.innerWidth/180 + 'px, '+ y*window.innerHeight/180 +'px, 0) rotate('+ r +'deg)' });
3、啟動原生應(yīng)用
背景:
在全民飛機大戰(zhàn)移動版的開發(fā)中,需要滿足一個需求,當(dāng)用戶點擊下載按鈕時,如果用戶已經(jīng)安裝了該APP,則直接啟動,否則跳到下載頁。
問題:
在網(wǎng)頁中如何啟動原生應(yīng)用?
解決:
首先通過URL Scheme嘗試打開本地APP,如果打開成功,網(wǎng)頁中setTimeout將會掛起,重新回到頁面時超時不再執(zhí)行,否則,如果打開失敗,則嘗試下載或進入下載頁面。
代碼:
(function(){ var iosURL = 'itms-apps://itunes.apple.com/cn/app/quan-min-fei-ji-da-zhan/id731871690?l=en&mt=8'; var androidURL = 'http://dlied5.qq.com/wefly/release/android/1.0.5/wefly_1.0.5.6_android_8CF5F95E.apk'; var iosScheme = 'tencentlaunch100539858:'; var androidScheme = 'webwx47a71dd8a1875943:'; var homePage = '/web201404/'; if(/iphone|ipod|ipad/i.test(navigator.userAgent)) { window.location = iosScheme; var startDate = new Date(); setTimeout(function(){ if(new Date() - startDate > 1000) return; window.location= iosURL; window.setTimeout(function(){ window.location= homePage; }, 100) } , 800); } else if(/android/i.test(navigator.userAgent)) { window.location = androidScheme; var startDate = new Date(); setTimeout(function(){ if(new Date() - startDate > 1000) return; window.location= androidURL; window.setTimeout(function(){ window.location= homePage; }, 1000) } , 800); } else { alert('非常抱歉,本游戲暫時只提供android和ios版。'); window.location= homePage; } })();
4、地理定位 HTML 5 Geolocatioin
地理定位是HTML 5的重要熱性之一,它提供了用戶的確切位置(經(jīng)緯度)。借助這個特性我們能夠開發(fā)基于地理位置的頁面。
HTML 5 Geolocatioin API 使用起來很簡單,我們通過下面的方式調(diào)用。
ar x=document.getElementById("demo"); function getLocation(){ if (navigator.geolocation){ navigator.geolocation.getCurrentPosition(showPosition); }else{x.innerHTML="Geolocation is not supported by this browser.";} } function showPosition(position){ x.innerHTML="Latitude: " + position.coords.latitude + "<br />Longitude: " + position.coords.longitude; }
代碼邏輯是先判斷你的設(shè)備支不支持這個接口,進而給出相應(yīng)的經(jīng)緯度。結(jié)合TGA城市賽這個案例,我們可以通過用戶在訪問頁面的時候,獲取其經(jīng)緯度,判斷用戶所在的城市,自動為其選中地址,減少操作步驟。如圖:
地址:
這里有一個小技巧,使用這個API我們只能獲取到經(jīng)緯度,那怎么轉(zhuǎn)換成對應(yīng)的具體地址呢?
1. 可以使用google的地圖API:
http://maps.google.com/maps/api/geocode/json?latlng=39.9,41.033&language=zh-CN&sensor=true
只要傳入正確的經(jīng)緯度,就能夠返回相應(yīng)的具體到街道的json數(shù)據(jù)。
2. 通過我們騰訊地圖的地址解析,可以做到具體地理位置字符跟經(jīng)緯度的轉(zhuǎn)換。
http://open.map.qq.com/javascript_v2/case-run.html#sample-geocoding-simple。
五、其它
1、字體圖標應(yīng)用設(shè)計環(huán)節(jié)的打通
問題:
字體圖標在這個項目中大量應(yīng)用,但面臨的一個問題是,輔助前端開發(fā)的配套工具已經(jīng)非常成熟,但設(shè)計環(huán)節(jié)的打通一直是一片空白。設(shè)計師在應(yīng)用字體圖標時還是只能以最原始的方式將圖標拖入設(shè)計稿,十分不便。
解決:
為設(shè)計師提供一款Photoshop插件,將字體文件導(dǎo)入插件,設(shè)計師可以直接點擊圖標插入設(shè)計稿,將字體圖標應(yīng)用的設(shè)計環(huán)節(jié)打通,該插件正在開發(fā)中。
2、當(dāng)transform碰上模糊
問題:
如下圖ball1所示,在android中,如果元素或其父元素應(yīng)用transform后,元素設(shè)置border-radius會變模糊。
解決:
將元素放大一倍后,再進行縮放(如果該元素后有文本節(jié)點,也可避免模糊)
body{padding: 20px;background:purple;-webkit-transform: translate3d(0px, 0px, 0px);} .ball1{width: 50px;height: 50px;border-radius:25px;margin-top: 20px;background: #fff;} .ball2{width: 100px;height: 100px;border-radius:50px;margin-top: 20px;background: #fff;-webkit-transform-origin:0 0;-webkit-transform: scale(0.5, 0.5);-webkit-backface-visibility:hidden;}
DEMO地址:
http://sy.qq.com/brucewan/feature/blur-radius.html
六、數(shù)據(jù)
1、用戶環(huán)境
從TGA移動游戲官網(wǎng)統(tǒng)計數(shù)據(jù)分析:
1)、抽樣50%訪問量統(tǒng)計,移動端用戶平均加速速度為70Kb/s;
2)、總加載時間為4.12s,86%的用戶愿意等待頁面加載完畢,52%的用戶抵達第二屏;
3)、這次滾屏支持用戶點擊游戲圖標或手指滑動,但只有7%的人會使用點擊操作,我們后續(xù)項目的設(shè)計中可以得到啟發(fā)。
本文地址:http://m.likemindfilms.com/tutorial/id1998.html