zoukankan      html  css  js  c++  java
  • H5实现摇一摇技术总结

    摇一摇遇到的问题

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

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

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

    squire

    二、摇晃不灵敏,需要用力摇晃手机才行

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

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

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

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

    2、单个方向的加速度差值满足要求即可:

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

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

    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))
    	) {}
    

    3、不太普遍的计算方式:

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

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

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

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

    这个功能在业务中并没有加入,考虑后期优化的时候添加进去,在用户摇晃的过程中也能震动,可以很大的提升用户体验,让手机震动,通过如下API:

    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不支持。

    QQ20170105-001330

    四、ios手机无法主动播放音频

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

    大概总结几点:

    1、兼容性

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

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

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

    ![屏幕快照 2016-12-23 下午2.19.59](http://7mj4a6.com1.z0.glb.clouddn.com/2016-12-23-屏幕快照 2016-12-23 下午2.19.59.png)

    ![屏幕快照 2016-12-23 下午2.23.04](http://7mj4a6.com1.z0.glb.clouddn.com/2016-12-23-屏幕快照 2016-12-23 下午2.23.04.png)

    格式支持

    目前主要支持四种格式:MP3、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

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

    五、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](Javascript date parsing on Iphone)有人提这个问题,这是一个回答:并不是所有的浏览器都支持上面的形式,最好的方法就是通过(- :)分隔符把日期分离,然后分别传给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;
    }
    

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

    六、用户交互反馈

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

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

    七、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](http://7mj4a6.com1.z0.glb.clouddn.com/2017-01-05-屏幕快照 2017-01-05 下午3.08.19.png)

    从中可以看出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);
        };
      

    八、总结

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

    关于音频的问题,是需要继续调研下的,如果大家知道原因麻烦也告诉我哦,查找了好几天了。。。

  • 相关阅读:
    Android 动画
    Eclipse设置软tab(用4个空格字符代替)及默认utf-8文件编码(unix)
    android ANR
    Android 服务端开发之开发环境配置
    安装Android sdk 4.4(19)出现问题的解决方案
    adb uninstall/pull/push 命令的使用总结
    Android学习笔记1 android adb启动失败问题 adb server is out of date. killing...
    Android.mk文件语法规范 原文
    Android MediaProvider数据库模式
    Android MVC模式
  • 原文地址:https://www.cnblogs.com/yangyoucun/p/6259552.html
Copyright © 2011-2022 走看看