H5实现摇一摇技术总结

摇一摇遇到的问题

1、如何对摇晃效果进行反馈

刚开始的处理方式是,摇晃过程当中不作任何处理,但后来反馈说这种效果很差,好像就没有摇动同样,若是声音也不响的话,就真的和什么都没发生同样。javascript

后来想了想,加入摇晃过程动画,就像微信的摇一摇同样,摇晃过程当中,会有上下移动的动画,这里加入了周围金币作跳跃运动的动画。css

squire

2、摇晃不灵敏,须要用力摇晃手机才行

摇晃灵敏度是个不太好控制的量,即要求不是很灵敏,好比,不能稍微碰一下就摇晃了,也不能使劲摇才能摇中,这就须要对X、Y、Z轴上的加速度进行合理利用,这里是几种网上常见的处理方式:html

一、计算一段时间中的X、Y、Z轴的平均值:

Math.abs( x + y + z - lastX - lastY - lastZ ) / diffTime * 10000

这种方式在网上最为常见,大多数的示例都采用的该方法。后来看到有人评论此方法可能致使摇一摇并不特别灵敏,好比x为正、y为负,就可能出现抵消的状况。html5

二、单个方向的加速度差值知足要求便可:

Math.abs(x-lastX) > speed || Math.abs(y-lastY) > speed

该方法也是shake库采用的计算方式,不过,该库的计算方式覆盖的更全面一些,也包括了斜向摇晃的判断,该库有近1000个start,使用人数也较多:java

this.options.threshold = 15;
deltaX = Math.abs(this.lastX - current.x);
deltaY = Math.abs(this.lastY - current.y);
deltaZ = Math.abs(this.lastZ - current.z);
if ((
    (deltaX > this.options.threshold) && 
    (deltaY > this.options.threshold)) || 
    (
        (deltaX > this.options.threshold) && 
        (deltaZ > this.options.threshold)
    ) || 
    (
        (deltaY > this.options.threshold) && 
        (deltaZ > this.options.threshold))
    ) {}

三、不太广泛的计算方式:

Math.sqrt( 
    ( x - lastX ) * ( x - lastX ) + 
    ( y - lastY ) * ( y - lastY ) + 
    ( z - lastZ ) * ( z - lastZ ) 
) / diffTime * 10000

这种方式相似于求三维空间中任意两点的距离,而后经过距离除以时间,获得速度的变化率,在摇晃过程当中摇晃的速度知足条件就表示中奖了。目前来看,这种方式更加科学一点,由于它更能体现出摇一摇的实际情景:摇的快一点,就能摇一摇。android

刚开始的时候采用的是第一种计算方式,计算在这一段时间内的平均值,可是在使用的过程当中发现并非很好用,设置的值过小就会比较灵敏,太大又不太灵敏,老是找不到一个合适的值知足条件。后来改用第三种方式,能够获得良好的摇一摇体验。ios

3、摇晃的同时让手机振动

这个功能在业务中并无加入,考虑后期优化的时候添加进去,在用户摇晃的过程当中也能震动,能够很大的提高用户体验,让手机震动,经过以下API:css3

navigator.vibrate

特征检测:

var supportsVibrate = "vibrate" in navigator;

使用方法:

navigator.vibrate(2000) // 震动2s
window.navigator.vibrate([
    100,30,100,30,100,200,200,30,
        200,30,200,200,100,30,100,30,100]); // 震动出莫尔斯电码的"SOS"效果

// 取消震动,赋值0或空数组便可
navigator.vibrate(0)

经过CanIuse查看支持状况,支持android4.4,ios不支持。git

QQ20170105-001330

4、ios手机没法主动播放音频

这里是一篇比较完善的关于HTML Audio的说明克服 iOS HTML5 音频的局限github

大概总结几点:

一、兼容性

iOS3中,移动版safari中就已经引入HTML音频

iOS6中,Apple增长了Web Audio API的支持

备注:到目前为止,iOS版本分布以下,因此,目前使用HTML音频基本没有兼容问题。

屏幕快照 2016-12-23 下午2.19.59

屏幕快照 2016-12-23 下午2.23.04

格式支持

目前主要支持四种格式:MP三、OGG、WAV 和 AAC。

Ogg Vorbis WAV PCM AAC
Internet Explorer 9 X X
Firefox X X
Chrome/Safari/移动版 Safari X X X

为了涵盖全部浏览器,最好是让全部的视频流都具备 Ogg Vorbis 和 AAC 两种格式。

为何没有包括 MP3?MP3 在进行商业传播时须要支付繁重的版税。

Ogg Vorbis 之因此压倒性地得到了个人喜好是由于它是开源的、无专利费而且免版税的。不过,只有 Firefox 支持它。

单音频流

移动版safari一次只能播放一个单音频流。移动版 Safari 中的 HTML5 媒体元素都是单例的,因此一次只能播放一个 HTML5 音频(和 HTML5 视频)流。Apple 为这一局限作过解释,但咱们推断这是为了减小数据费用(这也是大多数 iOS HTML5 其余局限的缘由所在)。

自动播放

在移动版 Safari 中加载的页面上,不能自动播放音频文件。音频文件只能从用户触发的触摸(单击)事件加载。preload、autoplay会忽略。

切换延迟

在初始化一个新的音频流时会有几秒的延时,这是由于 iOS 须要实例化一个新的音频对象。

解决方案

解决自动播放

当用户触摸屏幕的时候,便会加载音频,而后在摇晃手机时进行播放。

var shakeAudio = new Audio();
shakeAudio.preload = 'auto';
shakeAudio.src = 'xx';
document.body.addEventListener('touchstart', () => {
    shakeAudio.load();
});

解决单音频流 && 解决切换延迟

使用audio sprite

audio.sprite能够将多个音频合成一个音频,就像css sprite同样。

原理很直观。您须要存储每一个 sprite 的数据:开始点、结束点(或长度)和一个 ID。当您想要播放某个 sprite 时,须要将此音频流的 currentTime 设为开始位置并调用 play()。

var spriteData = {
    meow1: {
        start: 0,
        length: 1.1
    },
    meow2: {
        start: 1.3,
        length: 1.1
    },
    whine: {
        start: 2.7,
        length: 0.8
    },
    purr: {
        start: 5,
        length: 5
    }
};
audioSprite.currentTime = spriteData.meow2.start;
audioSprite.play();

这种方式能够解决单个音频与切换延迟的问题,由于只有一个音频加载,这也削减了HTTP请求。

清注意,更改 currentTime 并非百分百正确的。将 currentTime 设为 6.5,而实际获得的倒是 6.7 或 6.2。每一个 A sprite 之间须要少许的空间,以免寻找到另外一个 sprite 的尾部。添加这个空间会增长少量延时,若是流寻找到 6.4,而 sprite 开始于 6.8 秒。

在访问任何 audio sprite 以前,务必确保整个音频流已加载,由于若是音频流没有彻底加载,那么在想要访问已加载的流的任何一个部分时,那么这个流须要进行缓冲,并且还会在流加载过程当中发生延时。

注意,音频资源放到服务器可能也不会成功!

这是Chromium的一个bug:https://bugs.chromium.org/p/chromium/issues/detail?id=584562

备注:我在实际测试的出现问题,currentTime没法设置,一直都是0,即时赋值为3,但打印出来后依然为0,致使音频不能切换。

测试地址:http://img.youthol.top/audioTest-1.html

这个问题尚未找到具体缘由。应该和服务器设置有关,该问题已经浪费了好长时间去搜索,目前还没找到更具体的缘由。

5、ios手机Date兼容问题

背景:

在项目中有一个体验须要优化,若是活动在晚上12点开始,用户在12点以前进来,此时是不能参加的,提示活动时间还不到,可是若是到12点了呢?用户必须退出去而后在进来,这样才能看到最新的状态?

这样体验会差一点,最好的方式是:到达了12点,数据自动更新。用户能够直接参与抽奖!

实现思路:

为了实现这一个效果,采用的思路是:用户刚进入页面的时候会有一个时间戳,而后我拿到该时间戳进行解析,获取当天的时间属性(年月日),而后经过new Date()建立一个次日的凌晨时间,此时,用该时间与页面请求的时间作diff,利用setTimeout进行diff时间的倒计时,倒计时结束,自动执行数据更新。

遇到的问题

这种思路在android上是没有问题的,可是测试时发如今ios上不会更新数据,后来定时,是获取时间时有问题:

原本我是经过new Date("2016.12.27")这样的方式在chrome的测试控制台中测试的,能够拿到当天凌晨时间,因而便拿该时间去用,可是该时间在safari中会出错:

new Date("2016.12.27")
// Invalid Date
new Date("2016.12.27").getTime()
// NaN

这就遇到了一个好玩的事情,那safari下支持什么样的时间格式呢?下面便进行一些测试:

刨坑

safari:在safari浏览器中处理时间会产生很是有意思的效果,好比:

new Date("2016.12.27")
// Invalid Date

new Date("2016-12-27")
// Tue Dec 27 2016 08:00:00 GMT+0800 (CST)

new Date("2016/12/27")
// Tue Dec 27 2016 00:00:00 GMT+0800 (CST)

chrome:可是在chrome上,咱们测试一下:

new Date("2016.12.27")
// Tue Dec 27 2016 00:00:00 GMT+0800 (CST)

new Date("2016-12-27")
// Tue Dec 27 2016 08:00:00 GMT+0800 (CST)

new Date("2016/12/27")
// Tue Dec 27 2016 00:00:00 GMT+0800 (CST)

. 点的日期形式在safari上是不支持的

- 短线的日期形式返回值相同

/ 斜杠的日期形式返回值也相同

另一个更神奇的地方是,一样是ISO 8601日期格式形式,safari也获取不到:

new Date("2016-12-27 12:34:25")
// Invalid Date

stackoverflow有人提这个问题,这是一个回答:并非全部的浏览器都支持上面的形式,最好的方法就是经过(-,` :`)分隔符把日期分离,而后分别传给Date构造器:

var arr = "2010-03-15 10:30:00".split(/[- :]/),
date = new Date(arr[0], arr[1]-1, arr[2], arr[3], arr[4], arr[5]);

console.log(date);
//-> Mon Mar 15 2010 10:30:00 GMT+0000 (GMT Standard Time)

这样全部的浏览器都运行正常。不过,须要注意的是,月份要减1,GMT的时间月份0表示1月份。

一样,根据这篇文章:JavaScript new Date() NaN on iPhone,能够经过以下方式,也能够获取浏览器一致的效果:

mm/dd/yyyy hh:mm:ss

if (app.isAppleDevice()) {
    var dateParts = myDate.substring(0,10).split('-');
    var timePart = myDate.substr(11);
    myDate= dateParts[1] + '/' + dateParts[2] + '/' + dateParts[0] + ' ' + timePart;
}

关于时间问题坑仍是不小的,红宝书上对时间的描述也较多,也可参考。

6、用户交互反馈

在平时的项目中,通常要求有较快的用户反馈,可是在摇一摇项目中,有一些小的交互需求须要注意。

第一个就是该总结刚开始说的,延迟出现抽奖结果,这样更符合用户体验。摇一摇立马弹出反而更显突兀。

7、requestAnimationFrame

在业务中,有一个滚动公告需求,刚开始滚动公告采用setInterval的方式进行,可是当把切换其余浏览器tab一段时候后再次回来,发现公告是快速的滚动到某一位置。

缘由是setInterval在窗口退到后台时依然会执行。解决这个问题就须要requestAnimationFrame了。

  • requestAnimationFrame是用来解决动画渲染问题的,通常来讲,其渲染执行频率和浏览器渲染频率相同,都为60帧。
  • requestAnimationFrame发生在重绘前,浏览器在重绘时会提早通知requestAnimationFrame,这样咱们能够把DOM操做集中在一块儿,这样只发生一次重绘。
  • 隐藏或不可见元素,requestAnimationFrame将不进行重绘或回流。减小内存使用量。

虽然requestAnimationFrame不能够直接设置时间间隔,但能够经过时间判断来完成:

let lastTime = 0;
const scroll = () => {
    const now = Date.now();
    if ( now - startTime > during ) {
        startTime = now;
        this.shakeScrollCurrent--;
        this.showNoticeList = true;
        // 若是滚动到头了,此时会从新进入滚动
        // 防止在切换的过程当中出现动画,此时须要先把动画去除,而后在进行数量重置
        if ( ( this.shakeScrollCurrent ) % ( prizesLength + 1 ) === 0 ) {
            this.showNoticeList = false;
            this.shakeScrollCurrent = 0;
        }
    }
    window.requestAnimationFrame( scroll );
}
window.requestAnimationFrame( scroll );

兼容性:

屏幕快照 2017-01-05 下午3.08.19

从中能够看出android低版本(4.3)及如下是不支持该属性的,须要对此进行兼容,能够参考以下:

  • CSS3动画那么强,requestAnimationFrame还有毛线用?

    window.requestAnimationFrame = window.requestAnimationFrame ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          window.oRequestAnimationFrame ||
          window.msRequestAnimationFrame || function ( callback, element ) {
              var currTime = new Date().getTime();
              var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
              var id = window.setTimeout(function() {
                  callback(currTime + timeToCall);
              }, timeToCall);
              lastTime = currTime + timeToCall;
              return id;
          }
  • 动画requestAnimationFrame

    function (callback, element) {
          var start, finish;
          window.setTimeout(function () {
              start = +new Date();
              callback(start);
              finish = +new Date();
              self.timeout = 1000 / 60 - (finish - start);
          }, self.timeout);
      };

8、总结

摇一摇过程并不复杂,其实像这种活动更重要的是如何提高用户体验,好比在项目中发现,有的手机其支持加速事件,可是摇晃过程没有任何的反应。好比Android 6.0; PLK-AL10(HUAWEI),若是有这同款手机的童鞋能够试一试。说这些是提醒有作相关活动的童鞋,能够在项目中添加统计代码,上报该手机支持仍是不支持摇一摇,若是不支持也能够加入预警,这样能够及时获得反馈。若是不支持,能够考虑添加其余途径也能参与活动。

关于音频的问题,是须要继续调研下的,若是你们知道缘由麻烦也告诉我哦,查找了好几天了。。。

相关文章
相关标签/搜索