记录贰个线上bug,那么大家在页面onload后触发播放事件

运动端H5制作安卓和IOS的坑 持续更新…

序言:近来到庭合营社的H5页面创新意识比赛,又遇上很多页面在不相同系统上的坑。踩坑之余,觉得很多事先遭受的知识点都忘了,索性开一篇博文,把那一个坑都统一归结起来,持续搜集更新,于己利人,投砾引珠。

笔录3个线上bug!

1. ios种类手提式有线电话机不大概自动播放B威斯他霉素

以此是苹果系统限制,默许不允许自动播放音频,往往需求点一下触发play()事件才能播放。
那么大家在页面onload后触发播放事件:

document.getElementById('music').play();

到那边一般都能够播放音乐了,假若还尤其,很有或者是微信的界定。那时急需调用微信接口。
页面先引入:

<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>

下一场JS写入微信事件:

document.addEventListener("WeixinJSBridgeReady", function() {
    document.getElementById('music').play();
  }, false);

这般利用微信接口调用play()事件,能够周密消除ios音频不大概autoplay的标题。

题材场景:

iphone6
safari浏览器打开爱奇艺TV剧播放页,如你好,旧时光,然后间接点第2集,播放器一贯展现loading浮层,如下图所示

图片 1

image.png

预料结果应该是能够符合规律加载第三集录制并播放的。不过只要你进去播放页后,先点击第③集的播放按钮,等待录像播放后,再切第一集,又是足以健康播放的。

那是二个包容性难题,因为用iphone6
qq浏览器不可能复现。由此笔者首先作了二个总计,看哪样浏览器能够复现,针对ios和andorid各选了六款机型,总括如下:

  1. 机型:iphone8/iphone x/iphone7P,系统:ios11+
    safari、qq、百度、手百均没反常
  2. 机型:iphone6,系统:ios10
    qq没不通常,safari、百度、手百有题目
  3. 机型:iphone5,系统:ios8.1
    qq没不平常,safari、百度、手百非凡
  4. 机型:小米6,系统:android7
    qq、OPPO浏览器没有失常态,chrome、百度、手百有标题
  5. 机型:oppo r9,系统:android6
    qq没非常,chrome、百度、手百、系统自带浏览器有失水准

从以上来看,安卓chrome、百度、手百以及ios10之下的safari、百度和手百均是有标题的,QQ浏览器相比独特,在android和ios均表现符合规律。

2. ios种类摇一摇播放音响效果事件无效

在完成摇晃(引用了打包好的shake.js)手提式有线电话机触发某一音响效果那些必要时,发以后微信中,音效没有被触发。前面找到原因:在ios里并没有把自定义摇晃事件shake当成交互动作。而要播放音响效果,需求用户有相互动作。没有互动,音响效果就没被加载,那么我们先加载音响效果,结合地点的微信接口:

document.addEventListener("WeixinJSBridgeReady", function () {
  shakeMusic.load();
}, false);

load()过之后,再调用play()即可听到音响效果。

录制播放的长河

咱俩先分析下播放录制的流程,首先会先向后台发送鉴权请求,其实正是依据你的用户音信,判断是不是播广告、试看6秒钟或是不是要用券等等音讯,比较复杂,但说到底而言,都以为了得到1个录像的广播链接(假诺是鉴权后无法播放的,那就须要出示播放器的唤起浮层了,暂不考虑那种气象)。然后再分析以下二种操作场景的界别:
1. 打开始播放放页,直接点封面图的播放按钮
开拓页面包车型大巴经过中,js会提前发送鉴权请求获得广播的url地址(那是为了收缩用户开始播放时间而做的优化),然后将url赋值给video标签的src属性,所以打开页面后,video标签其实已经赋上首先集的广播地址了。然后大家点击封面图的播放按钮,执行点击事件的函数,最后调用video对象的play()方法,从而平常播放摄像。所以这一步,没有请求接口拿播放地址的操作。

2.打开始播放放页,点击封面图的播放按钮,播放后,再切点击第①集
前方的操作是同一的,来探视点击第三集的操作进度,其实正如相近,点击后供给头阵鉴权请求得到第①集的广播url,然后将url地址赋值到video的src属性,最后调用video的play()方法播放摄像。到现行反革命告竣,一切还都平常,
符合大家的预料,接下去看看第二种操作。

3. 开拓播放页,直接点击第①集
这一回,大家从未先播放第①集,而是直接想播放第①集录像,结果就GG了,播放器向来彰显loading浮层。对于loading浮层的呈现逻辑,应该是点击切换剧集就会展现,然后待得到广播url,赋值src属性,调用play()播放录制后就应该隐藏的,实现情势便是监听播放器的playing事件,一般播放器发送playing事件就意味着摄像第叁帧已经播放了。所以,loading浮层不消退的来头应该是播放器没有触发playing事件,可能说录制没有正规播放。

大家相比较一下操作场景1和场景3的差距,场景1在点击按钮时,video标签的src属性已经赋上,而气象3点击剧集,须要调用一个后台接口获得广播链接,然后赋值到src,调用play(),所以双方的差距应该是多了1遍呼吁鉴权的进程,那个请求供给一定的时间,所以大家猜度,是还是不是那几个小时太长导致。

为了模仿以上两种情景,写了3个简易的页面实行模拟。demo很是简单,正是一个播放器,加多少个着力的按钮,能够兑现录制的播报、暂停与切换。

图片 2

着力代码如下:

<!DOCTYPE html>
<html>
<head>
    <title>video兼容性测试</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
</head>
<script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script>
<script type="text/javascript" src="//static.qiyi.com/js/html5/js/lib/lib.2.0.1.min.js?sea1.2.min.js"></script>
<style>
    #num {
      width: 100%;
      height: 100%;
      text-align: center;
      font-size: 48px;
      font-weight: bold;
    }
</style>
<body>
<video id="video" src="http://qamp.qiyi.domain/static/03.mp4" width="100%" height="100%"
    controls   poster="http://qamp.qiyi.domain/static/03.jpg">
    不支持video标签显示这段文字
</video>
<div>
    <button type="button" onclick="play()"> 播放 </button>
    <button type="button" onclick="pause()"> 暂停 </button>
    <button type="button" id="video1" onclick="playVideo(this.id)"> 第一集 </button>
    <button type="button" id="video2" onclick="playVideo(this.id)"> 第二集 </button>
    <button type="button" id="video3" onclick="playVideo(this.id)"> 第三集 </button>
    <img src="http://qamp.qiyi.domain/static/loading.jpg" alt="图片 3" style="display:none">
    <div id="num"></div>
</div>
<script>
    albumData = {
        'video1': {
            'src' : 'http://qamp.qiyi.domain/static/01.mp4',
            'img' : 'http://qamp.qiyi.domain/static/01.jpg'
        },
        'video2': {
            'src' : 'http://qamp.qiyi.domain/static/02.mp4',
            'img' : 'http://qamp.qiyi.domain/static/02.jpg'
        },
        'video3': {
            'src' : 'http://qamp.qiyi.domain/static/03.mp4',
            'img' : 'http://qamp.qiyi.domain/static/03.jpg'
        }
    }
    function sleep(d){
        for(var t = Date.now();Date.now() - t <= d*1000;){};
    }
    function play() {
        $('video')[0].play()
    }
    function pause() {
        $('video')[0].pause()
    }
    function playVideo(tvid) {
        sleep(1.51)
        video = $('video')[0]
        video.src = albumData[tvid]['src']
        video.poster = albumData[tvid]['img']
        //setTimeout(() => video.play(),0)
        video.play()
        $('#num').text('playing');
    }
</script>
</body>
</html>

最终发现部分移动端浏览器对于录像的播音有如下限制条件:

  • 从接触事件始于,到调用video对象的play()方法,时间上无法抢先一千ms
  • setInterval函数里,只有首先次就调用play()才能不负众望,原因不明

3. ios种类不帮衬动画暂停样式(animation-play-state

H5页面一般都会有B培洛霉素,也会提供叁个转悠的音乐图标供用户打开关闭音乐。我们期望当用户点击音乐按钮时图标甘休旋转,再点图标顺着在此之前甘休的岗位再而三跑动画。animation-play-state是最方便的办法,不过,ios不补助。

此时此刻的化解方案是:音乐图标负责跑动画,图标父级成分负责记录下龙时的转动值。

干什么手提式有线电话机端浏览器会有那几个界定

那差不多要从手提式有线电电话机流量说起,笔者回想在上海高校学那会,2009年左右,那时候依旧PC时期,智能机还某个流行,流量也是那么些高昂,5块钱大概才30M,所以为了用户着想,浏览器禁止了autoplay和js的加载播放,防止流量偷跑的光景,而且在活动流量下播放录像,尽管是手动触发录制播放,浏览器也会重复弹窗让用户确认,确认后才放行操作。但随着流量越来越方便,苹果在IOS11本子撤除了该限制。
以下摘自苹果的文档:

    In Safari on iOS (for all devices, including iPad), where the user may
be on a cellular network and be charged per data unit, preload and
autoplay are disabled. No data is loaded until the user initiates it.
This means the JavaScript play() and load() methods are also inactive
until the user initiates playback, unless the play() or load() method
is triggered by user action. In other words, a user-initiated Play
button works, but an onLoad="play()" event does not.

android4.2也引入了与ios类似的方案,由mediaPlaybackRequiresUserAction参数控制,私下认可是YES,所以必须由用户触发的表现,调用play()方法才使得,而onload事件是无奈播放的,那约等于为啥手提式有线电话机浏览器不能通过js控制自动播放。
详细文书档案参考:http://fqk.io/issues-of-audio-video-in-webview/

html

<div class="music">
    <img class="musicImg" src="music.png">
</div>
时光上的限量

为了明显具体的时刻范围。在demo代码中有1个sleep函数,能够决定从点击到调play()方法的日子,在种种浏览器上实验后,计算如下:

  • Android端浏览器:
    QQ浏览器:60+s(暂不知道上限)
    chrome/百度/手百/360极速: 1000ms
  • IOS端浏览器:
    QQ浏览器:60+s(暂不知道上限)
    ios 10以下的safari/chrome/百度/手百: 1000ms
    ios11以上safari/chrome/百度/手百:无限制

于是乎总括了下爱奇艺TV剧播放页,切换剧集时,从点击到开始播放广告,大概是1200ms左右,所以抢先了1s的年华限制,导致play()被浏览器正是非用户行为,所以不能播放。

注:假使成功播放录像后,之后的操作就不曾时间限定了,所以当进入页面后先播放第①集,然后再切第②集,就不会一贯loading了

sass

.music {
  position: absolute;
  width: rem(64px);
  height: rem(64px);
  top: rem(66px);
  left: rem(15px);
  z-index: 1000;

  img {
    width: 100%;
  }
}

.musicRun {
  -webkit-animation: music 2.5s infinite linear 0.5s;
  animation: music 2.5s infinite linear 0.5s;
}

@-webkit-keyframes music {
  0% {}
  100% {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}

@keyframes music {
  0% {}
  100% {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}
至于在setInterval函数里调用play()

发现那个难点纯属巧合,于今也没精晓为啥在setInterval函数里调用play()会败北。

在写demo的时候,本想用setInterval实现二个倒计时的成效,setInterval会每隔一定时间调用有些固定函数intervalFunc,最终发现惟有在第一回调用的intervalFunc函数里实施video.play()才使得,前面再调用的都不或然播放录制,固然设置的岁月非常短也充裕,代码如下:

    function playVideo(tvid) {
        var count = 20  //单位ms
        var intervalTime = 10  //每隔多久执行一次
        $('#num').text(count);
        var inter = setInterval(function intervalFunc() {
            count = count - intervalTime ;
            $('#num').text(count);
            if (count == 0) {
                //只有第一次就走到这分支才能播放
                clearInterval(inter);
                video = $('#video')[0]
                video.play()
                video.pause()
                video.src = albumData[tvid]['src']
                video.poster = albumData[tvid]['img']
                //setTimeout(() => video.play(),0)
                video.play()
                $('#num').text('playing');
            }
        }, intervalTime);
    }

上面代码大约意思是从20ms开头倒计时,每隔10ms递减3回,每一遍减10ms,等到为0时,执行play(),可是退步了,20ms假诺是放在一块儿施行的代码里,是平昔不难点的。
困惑或者是因为后边推行的intervalFunc被认为是非用户触发的,所以被play请求被阻碍了。
在看H5站源代码时候,里面发送请求的代码都用了promise异步写法,于是改造了一晃demo,如下:

    function getSrc(tvid) {
        sleep(1.9)
        return new Promise(function (resolve, reject) {
            console.log(albumData[tvid])
            resolve(albumData[tvid])
        })
    }
    function playVideo(tvid){
        getSrc(tvid).then((data)=>{
            video = $('video')[0]
            video.src = data['src']
            video.poster = data['img']
            video.play()
        })
    }

改完后在一加6测试,发现用promise的写法是没难点的,结果同样,唯有sleep超越1s才会合世问题。
下一场在stackflow发现那样一篇小说,play-request-was-interrupted,里面说

图片 4

image.png

大致意思正是,绑定点击事件的函数,不要选择async异步写法,不然会失去之后允许录制播放的用户作为token,相当于会被浏览器认定为非人为操作造成力不从心播放。
于是自个儿将点击函数改为async异步形式,代码如下:

   function getSrc(tvid) {
        sleep(3.99)
        return new Promise(function (resolve, reject) {
            console.log(albumData[tvid])
            resolve(albumData[tvid])
        })
    }
    async function playVideo(tvid){
        await getSrc(tvid).then((data)=>{
            video = $('video')[0]
            video.src = data['src']
            video.poster = data['img']
            video.play()
            $('#num').text('playing');
        })
    }

在BlackBerry6上测试,发现使用async也是没难题的,唯有在跨越1s才会播放战败。

JS

var $img = $('.musicImg')
  var music = document.getElementById('music');
  var isPlaying = false
  running()
  $img.on('click', function() {
    !isPlaying ? running() : paused()
})

  function running() {
    music.play();
    $img.addClass('musicRun')
    isPlaying = true
  }

  function paused() {
    music.pause();
    var siteImg = $img.css('transform') //获取当前元素的动画改变,transform的值
    var siteWp = $('.music').css('transform')
    $('.music').css('transform', siteWp === 'none' ? siteImg : siteImg.concat('', siteWp))
    //由于父元素没有动画,所以每次赋值的时候,需要将上次父元素的状态加上
    $img.removeClass('musicRun')
    isPlaying = false
  }
怎么化解录制自动播放的界定

倘使产品非得必要形成自动播放,该如何是好啊?答案是在点击后登时调用play()方法,假若video原本有src的话,会播放以前录像,所以要霎时调用pause()方法,但有大概会报Uncaught
(in promise) DOMException: The play() request was interrupted by a call
to pause(),这么些错倒不要紧,非亲非故首要,参考play() request was
interrupted
,要暂停摄像,也足以先将video的src清空,那样也是能够的。
代码如下:

    function getSrc(tvid) {
        sleep(3.99)
        return new Promise(function (resolve, reject) {
            console.log(albumData[tvid])
            resolve(albumData[tvid])
        })
    }
    async function playVideo(tvid){
        //立即调用play
        try{
            $('video')[0].play()
            $('video')[0].pause()  
        } catch(e){
            console.log(e)
        }
        await getSrc(tvid).then((data)=>{
            video = $('video')[0]
            video.src = data['src']
            video.poster = data['img']
            video.play()
            $('#num').text('playing');
        })
    }

4.安卓微信打开页面时动画静止

H5页面动画很要紧。当小编布好了动画样式,用安卓微信打开发现页面静止不动,动画没有奏效。新进入页面没意义,刷新一下就过来。

最直接了当的消除方案:把动画提取出来,例如提到叁个running体制中,然后在页面load完了再把那个动画值赋上去。

window.onload = function() {
  $('.index').addClass('running')
};
的确消除手机录制自动播放难题

http://levisft.beats-digital.com
http://campaign.wandoujia.com/market/ifly/index.html?ch\_src\_share=pp\_aty\_wechatshare
https://yq.aliyun.com/mk/01/index.php
http://nigg.treedom.cn/?dskid=ccc003
http://m.creatby.com/v2/manage/book/bcz2jo/
http://h5.flyfinger.com/2016/tencentfun/
https://lieyu.qq.com/cp/a20170213ane/index-wx.html?
http://jzsg.lxustudio.cn/

说说音频的自动播放难点

最近不足为奇H5活动页面都会含有背景音乐,但音频也有像样的标题,具体的显示是在当先46%安卓机上是能够自动播放,可是在ios上会有限制,那该如何消除呢?

1. 办法一:监听touchstart事件,用户触摸屏幕后会自动播放

document.addEventListener('touchstart', function(){ 
    audio.play();
}, false);

措施二:注重微信的ready事件开始展览,能够缓解ios微信的难题

document.addEventListener("WeixinJSBridgeReady", function () {
    audio.play();
}, false);
参考文献:

https://developers.google.com/web/updates/2017/06/play-request-was-interrupted
https://segmentfault.com/a/1190000007864808

相关文章