为你的移动页面寻找一丝新意——手机互动网页项目总结(下)

 In 网页重构 on 2014-04-30 10:54:10 by 万技师

目录:

一、项目地址

二、布局
1、按屏滚动会碰到什么问题?
2、flexbox会碰到什么问题?
3、fixed定位需要慎用。

三、动画
1、CSS动画与Canvas动画性能优劣分析
2、当缩放动画碰上硬件加速
3、动画由Javascript来维护还是css维护的思考

四、特性
1、如何解决音频播放预加载与延时的问题
2、重力感应
3、启动原生应用
4、地理定位

五、其它
1、字体图标应用设计环节的打通
2、当transform碰上模糊

六、数据
1、用户环境

一、项目地址

1、TGA移动游戏官网

TGA城市拉力赛

 

2、UP+邀请函

3、天天酷跑里约进击版

二、布局

1、按屏滚动会碰到什么问题?

问题:

按屏滚动即用户滑动屏幕翻页,屏幕自动锁定到当前页。我们会碰到的问题:

1)、页面100%高度包含地址栏高度,当地址栏存在时,会有部分内容被隐藏,如下图所示:

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会碰到什么问题?

问题:

自适应高度/宽度的flex元素内,子元素尺寸以百分比为单位(如height:100%)无效。

解决:

需要将子元素设为绝对定位。

3、fixed定位需要慎用。

问题:

在ios系统的浏览器中,页面加载时偶尔会出现抖动情况。

解决:

使用绝对定位或flexbox模拟解决。

三、动画

1、CSS动画与Canvas动画性能优劣分析

问题:

目前移动端动画,特别在H5游戏与强交互性网站应用非常普遍,常用的实现方式是CSS(硬件加速)与Canvas,但缺乏完整的性能分析数据,我们在选择动画实现方式时还是存在纠结。

解决:

选用高中低不同配置不同系统的4款设备,进行了一轮测试:

1)、测试DEMO:

Canvas: 2个元素

Canvas:10个元素

DOM:2个元素

DOM:10个元素

2)、测试数据:

红色代表警告数据,一般60以上较为顺畅。

特征 \ 测试机型

iPhone 4s
(iOS6)

iTouch 4
(iOS5)

三星i9250
(Android 4.0)

三星gt s6358
(Android 2.2)

预计市场份额

中高端(>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)、测试结论:

a、CSS动画更为流畅、但内存占用过高,动画元素在5个以内更为推荐;

b、Canvas动画存在丢帧现象,这一现象在android中低端手机中表现更为明显;

c、5个以内动画元素,选用CSS动画,80%的设备帧频可达80以上。

2、当缩放动画碰上硬件加速

问题:

如下图所示,如何平滑地实现底部导航的缩放呢?

1)、不能直接用scale来缩放,这样会造成文字的缩放与虚化;

2)、不能通过 height的变化来更改高度,这样不能启用硬件加速。

解答:

需要分拆实现,高度变化用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维护的思考

近期制作的几个动画较多的网站,在动画的调整与维护上花了不少时间,于是有了以下思考。

1)、判断优劣维度

开发效率、维护、性能

2)、具体分析

a、由CSS维护。

实现:

结合transition和animation,将动画时间轴写死在CSS中。

开发效率:高。

结合现有配套工具,能够快速完成动画制作。

维护:差。

如果动画涉及回调函数执行,时间轴同时存在于CSS与Javascript中,维护成本会大大提升。如果涉及判断与循环,动画效果将同时由CSS与Javascript来维护,代价太高。

性能:高。

动画无需解析Javascript,减少DOM操作,性能较Javascript的方式更高。

b、由Javascript维护。

封装动画框架,底层调用CSS动画,时间轴由Javascript来维护。

开发效率:高。无需考虑各平台兼容性,配置参数即可完成动画,对于复杂动画,也可开发配套工具。

维护:好。动画效果、动画逻辑,全部由Javascript来维护,动画可控性更高,维护变得很轻松。

性能:较高。由于底层还是调用CSS动画,DOM操作次数有限,性能损耗并不会太高。

3)、总结。

对于弱交互,无逻辑的动画,可以选用CSS的方式。对于复杂动画,更推荐由Javascript来维护。

四、特性

1、如何解决音频播放预加载与延时的问题

问题:

通过对各个平台下进行音频特性测试,存在问题如下:

 

IOS6

Android2.3

Android4.0

Audio标签base64

不支持

不支持

不支持

预加载

不支持

支持

支持

自动播放

不支持

支持

支持

不同声音同时播放

支持

支持

支持

播放延时

Web audio

支持

不支持

不支持

解决:

问题/平台

IOS4-5

IOS6-7

Android2.3-4.2

预加载

不支持

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数据处理
 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();
 }

} 

调用:

new QAudio({src: './audio/pop1.mp3', base64: './audio/pop1.js', callback: 'cbPop1'});

示例:

2、重力感应

问题:

重力感应的实现相对简单,具体见如下代码。值得注意的是,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、启动原生应用

背景:

在全民飞机大战移动版的开发中,需要满足一个需求,当用户点击下载按钮时,如果用户已经安装了该APP,则直接启动,否则跳到下载页。

问题:

在网页中如何启动原生应用?

解决:

首先通过URL Scheme尝试打开本地APP,如果打开成功,网页中setTimeout将会挂起,重新回到页面时超时不再执行,否则,如果打开失败,则尝试下载或进入下载页面。

代码:

(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的重要热性之一,它提供了用户的确切位置(经纬度)。借助这个特性我们能够开发基于地理位置的页面。

HTML 5 Geolocatioin API 使用起来很简单,我们通过下面的方式调用。

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;
 }

代码逻辑是先判断你的设备支不支持这个接口,进而给出相应的经纬度。结合TGA城市赛这个案例,我们可以通过用户在访问页面的时候,获取其经纬度,判断用户所在的城市,自动为其选中地址,减少操作步骤。如图:

地址:

这里有一个小技巧,使用这个API我们只能获取到经纬度,那怎么转换成对应的具体地址呢?

1. 可以使用google的地图API:

http://maps.google.com/maps/api/geocode/json?latlng=39.9,41.033&language=zh-CN&sensor=true

只要传入正确的经纬度,就能够返回相应的具体到街道的json数据。

2. 通过我们腾讯地图的地址解析,可以做到具体地理位置字符跟经纬度的转换。

http://open.map.qq.com/javascript_v2/case-run.html#sample-geocoding-simple。

五、其它

1、字体图标应用设计环节的打通

问题:

字体图标在这个项目中大量应用,但面临的一个问题是,辅助前端开发的配套工具已经非常成熟,但设计环节的打通一直是一片空白。设计师在应用字体图标时还是只能以最原始的方式将图标拖入设计稿,十分不便。

解决:

为设计师提供一款Photoshop插件,将字体文件导入插件,设计师可以直接点击图标插入设计稿,将字体图标应用的设计环节打通,该插件正在开发中。

2、当transform碰上模糊

问题:

如下图ball1所示,在android中,如果元素或其父元素应用transform后,元素设置border-radius会变模糊。

解决:

将元素放大一倍后,再进行缩放(如果该元素后有文本节点,也可避免模糊)

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

六、数据

1、用户环境

从TGA移动游戏官网统计数据分析:

1)、抽样50%访问量统计,移动端用户平均加速速度为70Kb/s;

2)、总加载时间为4.12s,86%的用户愿意等待页面加载完毕,52%的用户抵达第二屏;

3)、这次滚屏支持用户点击游戏图标或手指滑动,但只有7%的人会使用点击操作,我们后续项目的设计中可以得到启发。

评论