十二年前,无论多么复杂的布局,在我们神奇的table面前,都不是问题;
十年前,阿捷的一本《网站重构》,为我们开启了新的篇章;
八年前,我们研究yahoo.com,惊叹它在IE5下都表现得如此完美;
六年前,Web标准化成了我们的基础技能,我们开始研究网站性能优化;
四年前,我们开始研究自动化工具,自动化测试,谁没玩过nodejs都不好意思说是页面仔;
二年前,各种终端风起云涌,响应式、APP开发都成为了我们研究的范围,CSS3动画开始风靡;
如今,CSS3动画、Canvas、SVG、甚至webGL你已经非常熟悉,你是否开始探寻,接下来,我们可以玩什么,来为我们项目带来一丝新意?
没错,本文就是以HTML5 Device API为核心,对HTML5的一些新接口作了一个完整的测试,希望能让大家有所启发。
目录:
一、让音乐随心而动 - 音频处理 Web audio API
二、捕捉用户摄像头 - 媒体流 Media Capture
三、你是逗逼? - 语音识别 Web Speech API
四、让我尽情呵护你 - 设备电量 Battery API
五、获取用户位置 - 地理位置 Geolocation API
六、把用户捧在手心 - 环境光 Ambient Light API
七、陀螺仪 Deviceorientation
八、Websocket
九、NFC
十、震动 - Vibration API
十一、网络环境 Connection API
http://sy.qq.com/brucewan/device-api/
一、让音乐随心而动 - 音频处理 Web audio API
简介:
Audio对象提供的只是音频文件的播放,而Web Audio则是给了开发者对音频数据进行分析、处理的能力,比如混音、过滤。
系统要求:
ios6+、android chrome、android firefox
实例:
http://sy.qq.com/brucewan/device-api/web-audio.html
核心代码:
var context = new webkitAudioContext();
var source = context.createBufferSource(); // 创建一个声音源
source.buffer = buffer; // 告诉该源播放何物
createBufferSourcesource.connect(context.destination); // 将该源与硬件相连
source.start(0); //播放
技术分析:
当我们加载完音频数据后,我们将创建一个全局的AudioContext对象来对音频进行处理,AudioContext可以创建各种不同功能类型的音频节点AudioNode,比如
1、源节点(source node)
我们可以使用两种方式加载音频数据:
<1>、audio标签
var sound, audio = new Audio();
audio.addEventListener('canplay', function() {
sound = context.createMediaElementSource(audio);
sound.connect(context.destination);
});
audio.src = '/audio.mp3';
<2>、XMLHttpRequest
var sound, context = createAudioContext();
var audioURl = '/audio.mp3'; // 音频文件URL
var xhr = new XMLHttpRequest();
xhr.open('GET', audioURL, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
context.decodeAudioData(request.response, function (buffer) {
source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
}
}
xhr.send();
2、分析节点(analyser node)
我们可以使用AnalyserNode来对音谱进行分析,例如:
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var analyser = audioCtx.createAnalyser();
analyser.fftSize = 2048;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);
function draw() {
drawVisual = requestAnimationFrame(draw);
analyser.getByteTimeDomainData(dataArray);
// 将dataArray数据以canvas方式渲染出来
};
draw();
3、处理节点(gain node、panner node、wave shaper node、delay node、convolver node等)
不同的处理节点有不同的作用,比如使用BiquadFilterNode调整音色(大量滤波器)、使用ChannelSplitterNode分割左右声道、使用GainNode调整增益值实现音乐淡入淡出等等。
需要了解更多的音频节点可能参考:
https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API
4、目的节点(destination node)
所有被渲染音频流到达的最终地点
思维发散:
1、可以让CSS3动画跟随背景音乐舞动,可以为我们的网页增色不少;
2、可以尝试制作H5酷酷的变声应用,增加与用户的互动;
3、甚至可以尝试H5音乐创作。
看看google的创意:
http://v.youku.com/v_show/id_XNTk0MjQyNDMy.html
二、捕捉用户摄像头 - 媒体流 Media Capture
简介:
通过getUserMedia捕捉用户摄像头获取视频流和通过麦克风获取用户声音。
系统要求:
android chrome、android firefox
实例:
捕获用户摄像头 | 捕获用户麦克风 |
http://sy.qq.com/brucewan/ |
http://sy.qq.com/brucewan/ |
核心代码:
1、摄像头捕捉
navigator.webkitGetUserMedia ({video: true}, function(stream) {
video.src = window.URL.createObjectURL(stream);
localMediaStream = stream;
}, function(e){
})
2、从视频流中拍照
btnCapture.addEventListener('touchend', function(){
if (localMediaStream) {
canvas.setAttribute('width', video.videoWidth);
canvas.setAttribute('height', video.videoHeight);
ctx.drawImage(video, 0, 0);
}
}, false);
3、用户声音录制
navigator.getUserMedia({audio:true}, function(e) {
context = new audioContext();
audioInput = context.createMediaStreamSource(e);
volume = context.createGain();
recorder = context.createScriptProcessor(2048, 2, 2);
recorder.onaudioprocess = function(e){
recordingLength += 2048;
recorder.connect (context.destination);
}
}, function(error){});
4、保存用户录制的声音
var buffer = new ArrayBuffer(44 + interleaved.length * 2);
var view = new DataView(buffer);
fileReader.readAsDataURL(blob); // android chrome audio不支持blob
… audio.src = event.target.result;
思维发散:
1、从视频拍照自定义头像;
2、H5视频聊天;
3、结合canvas完成好玩的照片合成及处理;
4、结合Web Audio制作有意思变声应用。
三、你是逗逼? - 语音识别 Web Speech API
简介:
1、将文本转换成语音;
2、将语音识别为文本。
系统要求:
ios7+,android chrome,android firefox
测试实例:
http://sy.qq.com/brucewan/device-api/microphone-webspeech.html
核心代码:
1、文本转换成语音,使用SpeechSynthesisUtterance对象;
var msg = new SpeechSynthesisUtterance();
var voices = window.speechSynthesis.getVoices();
msg.volume = 1; // 0 to 1
msg.text = ‘识别的文本内容’;
msg.lang = 'en-US';
speechSynthesis.speak(msg);
2、语音转换为文本,使用SpeechRecognition对象。
var newRecognition = new webkitSpeechRecognition();
newRecognition.onresult = function(event){
var interim_transcript = '';
for (var i = event.resultIndex; i < event.results.length; ++i) {
final_transcript += event.results[i][0].transcript;
}
};
测试结论:
1、Android支持不稳定;语音识别测试失败(暂且认为是某些内置接口被墙所致)。
思维发散:
1、当语音识别成为可能,那声音控制将可以展示其强大的功能。在某些场景,比如开车、网络电视,声音控制将大大改善用户体验;
2、H5游戏中最终分数播报,股票信息实时声音提示,Web Speech都可以大放异彩。
四、让我尽情呵护你 - 设备电量 Battery API
简介:
查询用户设备电量及是否正在充电。
系统要求:
android firefox
测试实例:
http://sy.qq.com/brucewan/device-api/battery.html
核心代码:
var battery = navigator.battery || navigator.webkitBattery || navigator.mozBattery || navigator.msBattery;
var str = '';
if (battery) {
str += '你的浏览器支持HTML5 Battery API
';
if(battery.charging) {
str += '你的设备正在充电
';
} else {
str += '你的设备未处于充电状态
';
}
str += '你的设备剩余'
+ parseInt(battery.level*100)+'%的电量';
} else {
str += '你的浏览器不支持HTML5 Battery API
';
}
测试结论:
1、QQ浏览器与UC浏览器支持该接口,但未正确显示设备电池信息;
2、caniuse显示android chrome42支持该接口,实测不支持。
思维发散:
相对而言,我觉得这个接口有些鸡肋。
很显然,并不合适用HTML5做电池管理方面的工作,它所提供的权限也很有限。
我们只能尝试做一些优化用户体验的工作,当用户设备电量不足时,进入省电模式,比如停用滤镜、摄像头开启、webGL、减少网络请求等。
五、获取用户位置 - 地理位置 Geolocation
简介:
Geolocation API用于将用户当前地理位置信息共享给信任的站点,目前主流移动设备都能够支持。
系统要求:
ios6+、android2.3+
测试实例:
http://sy.qq.com/brucewan/device-api/geolocation.html
核心代码:
var domInfo = $("#info");
// 获取位置坐标
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition,showError);
}
else{
domInfo.innerHTML="抱歉,你的浏览器不支持地理定位!";
}
// 使用腾讯地图显示位置
function showPosition(position) {
var lat=position.coords.latitude;
var lon=position.coords.longitude;
mapholder = $('#mapholder')
mapholder.style.height='250px';
mapholder.style.width = document.documentElement.clientWidth + 'px';
var center = new soso.maps.LatLng(lat, lon);
var map = new soso.maps.Map(mapholder,{
center: center,
zoomLevel: 13
});
var geolocation = new soso.maps.Geolocation();
var marker = null;
geolocation.position({}, function(results, status) {
console.log(results);
var city = $("#info");
if (status == soso.maps.GeolocationStatus.OK) {
map.setCenter(results.latLng);
domInfo.innerHTML = '你当前所在城市: ' + results.name;
if (marker != null) {
marker.setMap(null);
}
// 设置标记
marker = new soso.maps.Marker({
map: map,
position:results.latLng
});
} else {
alert("检索没有结果,原因: " + status);
}
});
}
测试结论:
1、Geolocation API的位置信息来源包括GPS、IP地址、RFID、WIFI和蓝牙的MAC地址、以及GSM/CDMS的ID等等。规范中没有规定使用这些设备的先后顺序。
2、初测3g环境下比wifi环境理定位更准确;
3、测试三星 GT-S6358(android2.3) geolocation存在,但显示位置信息不可用POSITION_UNAVAILABLE。
六、把用户捧在手心 - 环境光 Ambient Light
简介:
Ambient Light API定义了一些事件,这些时间可以提供源于周围光亮程度的信息,这通常是由设备的光感应器来测量的。设备的光感应器会提取出辉度信息。
系统要求:
android firefox
测试实例:
http://sy.qq.com/brucewan/device-api/ambient-light.html
核心代码:
这段代码实现感应用前当前环境光强度,调整网页背景和文字颜色。
var domInfo = $('#info');
if (!('ondevicelight' in window)) {
domInfo.innerHTML = '你的设备不支持环境光Ambient Light API';
} else {
var lightValue = document.getElementById('dl-value');
window.addEventListener('devicelight', function(event) {
domInfo.innerHTML = '当前环境光线强度为:' + Math.round(event.value) + 'lux';
var backgroundColor = 'rgba(0,0,0,'+(1-event.value/100) +')';
document.body.style.backgroundColor = backgroundColor;
if(event.value < 50) {
document.body.style.color = '#fff'
} else {
document.body.style.color = '#000'
}
});
}
思维发散:
该接口适合的范围很窄,却能做出很贴心的用户体验。
1、当我们根据Ambient Light强度、陀螺仪信息、当地时间判断出用户正躺在床上准备入睡前在体验我们的产品,我们自然可以调整我们背景与文字颜色让用户感觉到舒适,我们还可以来一段安静的音乐,甚至使用Web Speech API播报当前时间,并说一声“晚安”,何其温馨;
2、该接口也可以应用于H5游戏场景,比如日落时分,我们可以在游戏中使用安静祥和的游戏场景;
3、当用户在工作时间将手机放在暗处,偷偷地瞄一眼股市行情的时候,我们可以用语音大声播报,“亲爱的,不用担心,你的股票中国中车马上就要跌停了”,多美的画面。
那么,各位看官起来摇一摇,活动活动筋骨,热热身。。。(PS:为什么我会联想到喝前摇一摇,吃(fan)货(tong)天赋?)。
简介:
装逼说法:设备移动传感器。
通俗说法:检测设备运动及运动状况,可以通过该接口获取到设备运动的方向和速度等相关信息
核心代码:
这货其实就是一个事件,简单地通过事件监听就可以获取到相关的设备运动信息
window.addEventListener("devicemotion", function(event) {}, false);
最简单的设备运动,我们基本上可以认为是前后、左右、上下这么6个方向上的,装换成3D环境中来讲的话,就是在x、y和z轴上运动,这三个轴上的信息,都包含在了event对象中。我们可以通过这种方式获得
window.addEventListener("devicemotion", function(event) { var eventacceleration = event.acceleration; document.querySelector('#devicemotion').innerHTML = "acceleration:
"+ eventacceleration.x+"
"+ eventacceleration.y+"
"+ eventacceleration.z }, false);
想看效果的话,这个时候就可以掏出手机,扫描下面的这个二维码
http://aaaa.sinaapp.com/deviceDemo/acceleration.html
不要问我为什么手机躺在桌面上,上面的数字还在不停地抖动,咱们文化人应该都知道什么叫相对静止。上面的三个不断变化的数字,就是你设备的运动数据了。如果你在惊讶为什么我都已经移动过手机,为什么上面的数字貌似也没什么大的变化~那么我会告诉你说:有本事你的眼睛跟着你的手机一样快速移动啊,运动过程中你就能发现这个值的变化了。没有这个本事吧!~自己尝试修改下那个demo。把运动中值的变化都记录下来看看。。。(PS:反正我不弄。)
还有一种比较理想的设备运动方式就是不产生位移,只是快速的旋转手机。这个旋转的信息同样也被反馈在了event对象中
window.addEventListener("devicemotion", function(event) { eventrotationRate = event.rotationRate; document.querySelector('#devicemotion').innerHTML = 'rotationRate:
'+ eventrotationRate.alpha+'
'+ eventrotationRate.beta+"
"+ eventrotationRate.gamma }, false);
同样的,下面的二维码也就是为了说明上面的这段代码,alpha、beta以及gamma就是旋转设备时的加速度。
http://aaaa.sinaapp.com/deviceDemo/rotationRate.html
可能有的童鞋会觉得~有了这个旋转应该就能实现一些手机左右旋转产生视差的效果了,其实是不行的~因为这货和上面的一样,设备停止后,我们可以认为他的值归0~。真正想要实现手机旋转视差,我们需要用到的就是另外一个event的属性accelerationIncludingGravity。从字面上理解这个属性就是重力加速度。
window.addEventListener("devicemotion", function(event) { eventaccelerationIncludingGravity = event.accelerationIncludingGravity; document.querySelector('#devicemotion').innerHTML = "accelerationIncludingGravity:
"+ eventaccelerationIncludingGravity.x+"
"+ eventaccelerationIncludingGravity.y+"
"+ eventaccelerationIncludingGravity.z }, false);
下面的这个二维码应该可以帮助到你来理解这个属性
http://aaaa.sinaapp.com/deviceDemo/accelerationIncludingGravity.html
至此,devicemotion这个事件就差不多了,可见,这货功能还是蛮强大的,如果你有一定的的数学和物理学的基础,那么,轻巧几行代码,实现个高尔夫球游戏玩玩,立马给人感觉高大上了。看谁还敢说程序员没有妹纸。。
通过这个devicemotion吧,还能实现些什么就各自去脑洞大开吧,比如说玩烂了的摇一摇,设备倾斜视差,重力感应类的小游戏。我们貌似还能通过这个属性,去做个基于HTML5的计步器玩玩?
其实,devicemotion还有一个好基友,这里也推荐给大家看一眼吧:
这个事件和devicemotion的使用方法基本一致
window.addEventListener("deviceorientation", function(event) { document.querySelector('#deviceorientation').innerHTML = event.alpha+'
'+ event.beta+"
"+ event.gamma+'
'; }, false);
这里的alpha、beta和gamma所指的就是手机设备的各种倾斜角度,如果你不能理解哪个是alpha、哪个是beta和gamma,那么你还是自己掏出手机,扫描下这个二维码。自己感受吧。(PS:我是真不爱画图,可能辛苦半天画出来的图,也有童鞋没明白)。
有前面devicemotion好基友的帮忙,似乎能做的东西又可以更多一点了。咱们前面貌似提及过“喝前摇一摇”。摇完之后,自然是要打开瓶盖痛饮一番了,结合旋转加速度,我们是不是可以尝试些一个拧瓶盖比赛的游戏了?怕长胖?要不,拧个螺丝钉也行。
说到游戏,特别上面还提及到了比赛。那么实时通信必然是不可少了。以往为了达到实时通信,也有不少的方法:轮询、长轮询、长连接、flash等各种方式。这些方法各自的弊端也是比较明显的,这里就不逐个展开细说了。这里主要给大家提交到的就是下面这货:
简介:
浏览器与服务器全双工通信(full-duplex)。
核心代码:
var ws = new WebSocket('ws://127.0.0.1:8808/');//建立服务器连接ws.onopen = function(){ systemInfo.innerHTML = '和websocket服务器连接成功
'; }//接收到服务器返回的数据ws.onmessage = function(e){ systemInfo.innerHTML += ''+e.data+'
'; }//断开服务器连接ws.onclose = function(){ systemInfo.innerHTML += 'WebSocket服务器连接关闭
'; }//ws发生错误ws.onerror = function(e){ console.log(e); systemInfo.innerHTML += 'WebSocket发生错误
'; }
对于webSocket,有兴趣了解更多的朋友直接转移到我的另外一篇文章《移动前端系列——websocket实时互动小游戏》
websocket的数据发送和接收,其实也都是依赖于网络的,webSocket自己也对网络状况这块通过事件的方式做出了相应,比如说上面的onclose、onerror。但是就目前来说,很多页面上可能并没有用webSocket,这么一来,对于网络何时断开了,可能捕获就没那么容易了,更何况,webSocket在网络信息这块,可能还并不是那么的完美。比如说,我想获取当前连接的网络类型。
网络类型这块我们暂且先放半分钟,我们先看一下如何动态判断网络连接是否断开,所指动态判断网络连接断开,指的是,页面不刷新的情况下,实时地知道当前网络是否可用。以往,我们想得到这种数据,我们一般的实现方法是通过轮询去访问服务器上的一个文件,如果文件正常响应,说明网络是通的;如果文件不响应,那么就认为网络是不通的。这种方式感觉挺靠谱,但是可以想象,轮询请求,会耗费多少服务器资源。下面这货出来之后,以往的这种方式就可以进博物馆了。
简介:
判断当前网络是否有效
用法也是相当地简单:
if (navigator.onLine) { alert('online'); } else { alert('offline'); }
我们可以通过事件的监听,去实时获取到网络状态的变化
window.addEventListener('offline', function(e) {alert('offline');}) window.addEventListener('online', function(e) {alert('online');})
这样以来,实时获取网络状态俨然和服务器已经没有半毛钱关系了。
好了,半分钟时间已到,我们回头来看看我前面说的那个问题,获取网络类型,所指网络类型,值的是2G、3G、4G、蓝牙等等。其实,我个人觉得,这个网络类型完全可以放到online那个事件对象里面去。但是很遗憾,至少目前没放,暂不清楚是基于什么考虑的。我们目前想要获取这个网络类型,需要通过另外一个属性
connection也是navigator的一个属性,他包含了一些当前连接的基本信息,其中connection.type值的就是当前的网络类型。其值包含了一下几个:
bluetooth cellular ethernet none wifi wimax wimax unknown
当网络类型发生变化时,同样的也是可以通过事件监听的方式来实时获取到网络类型。代码实现如下:
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; var type = connection.type; function updateConnectionStatus() { alert("网络由 " + type + " 变化为 " + connection.type); } //监听网络类型发生变化 connection.addEventListener('typechange', updateConnectionStatus);
有了这些,我们就可以根据不同的网络环境去加载不同的资源去最大程度上丰富用户体验。
我们上面所提及到的,很多都是和网络(互联网)相关,但是有的时候,我们想在没有网络的环境下,也想传送一些数据该怎么办。这种情况可能在不久的将来,在我们的网页里面也能实现了,这个就是接下来的NFC(为什么我会想到KFC)
简介:
近场通信/近距离无线通信技术。
其实说实在的,这个功能我并没有在我的页面里面调试出来。主要的一个原因是~这货目前只是在firefox os系统里面的firefox浏览器里面实现了,手头上没这设备。不过从他们官网的例子中,我大概地可以感受得到这货的好用,有兴趣的同学自行前往学习:https://developer.mozilla.org/en-US/docs/Web/API/NFC_API/Using_the_NFC_API。
当然了,如果你手头上刚好有两部带有NFC芯片的三星手机,可以试试。解锁屏幕,将两部三星手机后壳贴近,听见“滴”的一声后,注意看屏幕上的变化。然后点击屏幕,再去看看另外一个手机上发生了什么。
清脆“滴”的一声,多好的用户行为反馈,忍不住还多尝试了几次,忽然就感觉这种靠声音的提醒,似乎忽略了一些失聪的人群。如果加上点震动的话~似乎就完美了。不是于是乎,咱们本片的最后一个话题引出。
简介:
设备震动
核心代码:
navigator.vibrate = navigator.vibrate || navigator.webkitVibrate || navigator.mozVibrate || navigator.msVibrate; navigator.vibrate(value); navigator.vibrate(0);
vibrate的参数为震动的时间,
如果值为0,说明停止震动
值为一个数组的话,奇数项表示的是震动时间,偶数项表示的为间隔时间。
比如:vibrate(1000,1000,2000) 就是表示震动1秒之后暂停1秒,再震动2秒
有兴趣的,扫下这个二维码可体验
http://1.aaaa.sinaapp.com/deviceDemo/Vibration.html
一口气说了这么一堆,其实后面的这几个点,都还在标准化的路上,不同机型/平台支持不太一致,在实际项目中使用还是要注意点区别对待的~你们先回味着,我一个人先伤一会儿心去。
参考文献: source:http://tgideas.qq.com/webplat/info/news_version3/804/7104/7106/m5723/201506/354489.shtml
https://developer.mozilla.org/en-US/docs/Web/API
http://webaudiodemos.appspot.com/
http://www.w3.org/2009/dap/