天天看点

H5短视频滑动播放video

用户下滑,加载新的视频播放,上滑看历史页面。

1、布局,

     手势下上滑要整体上下滑动,滑动距离不够,需要自动回退到初始播放页面,滑动大于最小距离,则需要翻页。

      使用<ul><li></li><li></li>...</ul>

ul{
 width:100%;
transform:translateY(0)
}
li{
    width: 100%;
    height: 100vh;
    display: -webkit-box;
    display: -webkit-flex;
    display: flex;
    -webkit-box-align: center;
    -webkit-align-items: center;
    align-items: center;
    -webkit-box-pack: center;
    -webkit-justify-content: center;
    justify-content: center;
    color: #fff;
    box-sizing: border-box;
    overflow: hidden;
    position: relative;
    -webkit-overflow-scrolling: touch
}
           

2、手势滑动

    手势加速度,手势滑动距离    

/**
     * 滑动处理
     */
    function Touch() {
        this.init();
    }
    Touch.fn = Touch.prototype;
    Touch.fn.init = function (params) {
        var self = this;
        dataStore.lastY = dataStore.translateY;//记录当前位移
        self.startY = 0;
        self.moveY = 0;
        // self.lock = 0;//如果在回调或上升阶段不允许滑动.
        self.bodyHeight = window.innerHeight;
        self.resetLimitY();
        self.isIOS = /ip(?:hone|ad|od)/.test(navigator.userAgent.toLowerCase());
        ['touchstart','touchmove','touchend'].forEach(function (str) {
            document.addEventListener(str,self[str].bind(self));
        });
    }
    Touch.fn.touchstart = function(e){
        var self = this;
        if(dataStore.lock) return;
        self.startY = e.touches[0].pageY;
        self.start = Date.now();//标识滑动起始时间,也用于标识滑动start
    }
    Touch.fn.move = function (y) {
        dataStore.translateY = y;
    }
    Touch.fn.touchmove = function(e){
        var self = this;
        if(dataStore.lock||!self.start) return;//锁定了,或者没有start,主要是手势一直滑动情况,已经加速度划走了,手势需要松开再重新开始
        self.moveY = e.touches[0].pageY - self.startY;
        self.move(self.moveY+ dataStore.lastY);   
        self.detect();     
    }
    Touch.fn.detect = function(isEnd){
        var self = this;
        // console.log(self.moveY+"  "+(Date.now()-self.start));
        var a = Math.abs(self.moveY)/(Date.now()-self.start)>=0.5;
        if(isEnd){
            if(a){
                self.limitY = 0;
            }
            self.movend();
            return;
        }
        if(self.isIOS&&a){//IOS,可以在touchmove时直接滑动,体验流畅。
            self.limitY = 0;
            self.movend();
        }
        
    }
    Touch.fn.resetLimitY = function () {
        this.limitY = this.bodyHeight/3;//位移多少才下滑
    }
    Touch.fn.touchend = function (e) {
        var self = this;
        if(dataStore.lock||self.moveY==0||!self.start) return;
        self.detect(1);     
    }
    Touch.fn.movend = function () {
        // if(dataStore.lock) return;
        // dataStore.lock = 1;
        var self = this;
        /***
         * 最后上下位移小于最小值则还原为上一次位移,
         * 否则,那么就需要上移或下移一个body宽度,上移则translate加,下移在减去一个body
         * 这里是计算出了应该位移高度。
         */
        var transformY = Math.abs(self.moveY)<self.limitY?dataStore.lastY:dataStore.lastY+self.bodyHeight*(self.moveY>0?1:-1);
         
        /***
         * 还需计算最大下滑高度和最大上滑高度
         */
        var listUL = document.querySelector(".quan_vcd_list");
        var listHeight = listUL.getBoundingClientRect().height;
        
        //如果是最后一个li,则不能下滑,
        var maxBottom = (listHeight - self.bodyHeight)*-1;
        var lastComputeY = transformY>0?0:transformY<maxBottom?maxBottom:transformY;
        //停止滑动之后,自动滚动距离,transition
        listUL.classList.add('trans');
        
        if(lastComputeY<=0){
            var d = lastComputeY-dataStore.lastY;
            d&&events.trigger("touch_move",d,(-lastComputeY/self.bodyHeight));
        }
        self.start = 0;
        var raf = window.requestAnimationFrame|| window.webkitRequestAnimationFrame;
        raf(function () {
            self.move(lastComputeY);
            self.moveY = 0;
            dataStore.lastY = lastComputeY;//记录确定的位置
            if(listHeight+dataStore.lastY<=self.bodyHeight){
                events.trigger("turnPage");
            }
            setTimeout(function () {
                listUL.classList.remove("trans");
                dataStore.lock = 0;
                self.resetLimitY();
            },500);
        });
    }
           

3、音视频播放。在微信上不能直接同时播放音频和视频,加上属性x5-video-can-play-with-audio="true" 然后先播放音频再播放视频,则OK了。

<video x5-video-can-play-with-audio="true" :class="feedData.hide?'video_hide':''" :src="feedData.playurl" preload="auto" type="video/mp4" width="100%" webkit-playsinline="true" playsinline="true" loop="loop"></video>     

4、一直往下滑动,页面结构越来越大,响应越来越慢,所以需要适时隐藏一些不用的节点,display:none; 规避页面的大dom结构的重排和重绘。

5、ios上同时播放的视频不能超过16个,大于16的video,无法播放,总是失败。需要滚动适时处理掉播放的视频。,保持同时不能大于16个视频。

exports.init = function (opt) {
        var config = {
            props:['feedData','index'],
            data:function () {
                return {
                    play_btn:0,
                    bg_name:"",
                    anim_like:[],
                    vloading:0
                }
            },
            mounted:function(){
                addEvent(this);
                this.stall = 0;
                this.loaderror = 0;
            },
            methods:{
                playBtn:function(){
                    this.play();
                },
                onerror:function(){
                    errors(this,"error:");
                },
                onbort:function(){
                    errors(this,"bort:");
                },
                onstalled:function () {
                    var self = this;
                    if(!self.feedData.start) return;
                    self.vloading = 1;
                    self.play();
                    self.stall++;
                    if(self.stall==2){
                        util.showTip("网络有点慢哦~");
                        store.report(27,1);
                    }
                },
                onplaying:function(){
                    var self = this;
                    compute(self);
                    self.play_btn = 0;
                    self.wa("138146.34.1");
                    store.report(27,0);
                },
                onpause:function(){
                    setPlay(this,1);
                },
                waiting:function () {
                    var self = this;
                    clearInterval(self.timer);
                    self.loadTimes = 0;
                    self.timer = setInterval(function () {
                        self.loadTimes++;
                        if (self.loadTimes >= 2) {//连续3次未播放,当作是卡住了
                            self.aPause();
                            self.vloading = 1;
                        }
                    }, 1800);
                },
                ondurationchange:function(){
                    compute(this);
                },
                onloadedmetadata:function(){
                    compute(this);
                },
                ontimeupdate:function(){
                    timeupdate(this);
                },
                aPause:function(){
                    var self = this;
                    self.audio&&self.audio.pause();
                },
                aPlay:function(){
                    var self = this;
                    self.audio&&self.audio.play();
                },
                pause:function(){
                    var self= this;
                    self.video.pause();
                    self.aPause();
                    self.vloading = 0;
                    clearInterval(self.timer);
                },
                play:function(isMove){
                    videoPlay(this,isMove);
                },
                checkLoading:function(){
                    checkLoading(this);
                },
                show_ani:function (e) {
                    showAni(this,e);
                },
                click_pause:function (e) {
                    clickPause(this,e);
                },
                onx5videoexitfullscreen:function (params) {
                    this.video.play();
                },
                hideAll:function () {
                    console.log("hideAll");
                }
                
            }
        };
        Vue.component('video-com',util.assign(config,opt));
    }
    /**
     * 视频暂停
     * @param {*} self 
     */
    function clickPause(self,e) {
        var feed = self.feedData;
        if(feed.goods_show){
            feed.goods_show = 0;
            feed.hideOpt = 0;
            return;
        }
        if(feed.input_show==0&&feed.comments_show){
            feed.hideOpt = 0;
            feed.comments_show = 0;
            return;
        }
        if(feed.input_show){//评论的时候,不允许操作。
            return;
        }
        if(self.dt>0){
            self.show_ani(e);
            return;
        }
        self.dt = setTimeout(function () {
            self.dt = 0;
        },400);

        if(self.t||Date.now-self.dbclick<1500){//dbclick之后,1.5秒不响应点击播放暂停
            return;
        }
        self.t = setTimeout(function () {
            self.t = 0;
            if(self.play_btn){
                self.play();
            }else{
                self.pause();
            }
        },600);
    }
    /**
     * 双击视频动画
     * @param {*} self 
     */
    function showAni(self,e) {
        clearTimeout(self.t);
        self.t = 0;
        clearTimeout(self.dt);
        self.dt = 0;
        self.dbclick = Date.now();
        var feed = self.feedData;
        if(!(feed.commentid>0)&&!self.praising){
            self.praising = 1;
            store.add_praise(feed,function () {
                self.praising = 0;
            },detailUtil.getOnePic(feed));
        }
        var like = self.anim_like;
        var len = like.length;
        if(len>=8){
            like.splice(0,len-5);
        }
        like.push({x:e.pageX-100,y:height*this.index+e.pageY - 100});
    }
    /**
     * 检测下载是否完成
     * @param {*} self 
     */
    function checkLoading(self) {
        var interval = window.setInterval(getLoaded,100);
        // 获取视频已经下载的时长
        function getLoaded() {
            var end = 0;
            try {
                end = parseInt(self.video.buffered.end(0) || 0)+2;
            } catch(e) {
            }
            if(end>=self.duration){
                clearInterval(interval);
                self.loadedAll = 1;
                var nextItem = store.store.feedList[self.index+1];
                if(nextItem){//存在下一条
                    //没有播放视频,既还没有加载完成。
                    if(!nextItem.playurl) nextItem.playurl = nextItem.videourl;
                    //有背景音乐,但是播放的背景音乐未加载完成,则开始加载
                    if(!nextItem.bgmurl_p&&nextItem.bgmurl){
                        nextItem.bgmurl_p = nextItem.bgmurl;
                    }
                }

            }
            return end
        }
    }
    /**
     * 播放视频
     * @param {*} self 
     */
    function videoPlay(self,isMove) {
        self.play_btn = 0;
                    
        self.aPlay();
        
        isMove&&(self.video.currentTime = 0);
        if(self.audio){
            self.video.volume = 0;
            self.video.muted =1;
        }
        var feed = self.feedData; 
        if(!feed.playurl){
            feed.playurl = feed.videourl;
            self.video.load();
        }
        self.video.play();
        isMove&&setTimeout(function (paras) {
            
            if(isIOS&&!feed.start&&window.isXCX==1){
                self.play_btn = 1;
                clearInterval(self.timer);
            }else if(!feed.start){
                setTimeout(function () {
                    if(feed.start) return;
                    setPlay(self,1);
                    clearInterval(self.timer);
                },2000);
            }
        },1000);
        self.waiting();
    }
    /**
     * 播放中处理
     * @param {*} self 
     */
    function timeupdate(self) {
        var ct = self.video.currentTime;
        if(!self.duration) return;
        self.feedData.play_time = ct*100/self.duration;//设置到住storage
        // console.log("timeupdate");
        self.loadTimes>=2&&self.aPlay();
        var feed = self.feedData;
        if(!feed.start&&ct>0){
            feed.hide = 0;//video is hide or show
            feed.start = 1;//video is start ,should hide poster
        }
        self.vloading = 0;
        self.play_btn = 0;
        self.loadTimes = 0;
        self.loaderror = 0;
        if(!self.move&&ct+1>=self.duration/2){
            self.feedData.goodsMove = 1;
            self.move = 1;
        }
        if(Math.round(ct)%3==0){
            self.wa("138146.35.1");
        }
        if(!self.waDone&&Math.ceil(ct+1)>=self.duration){
            self.wa("138146.9.1");
            self.waDone = 1;
        }
    }
    /**
     * 处理视频和背景音乐
     * @param {*} self 
     */
    function addEvent(self) {
        self.$nextTick(function () {
            self.video = self.$el.querySelector("video");
            var arry = ['stalled','playing', 'timeupdate', 'abort', 'error','durationchange','loadedmetadata','pause','x5videoexitfullscreen'];
            arry.forEach(function (str) {
                self.video.addEventListener(str,self['on'+str]);
            });
            if(self.index==0){
                loadWX(function (env) {
                    if(env&&isAndroid) return;
                    self.play();
                });
            }
        });
        var feed = self.feedData;
        handleBGM(feed,self);
        
        events.listen("touch_move",function (direct,i) {
            handleMove(self,feed,direct,i);
        });
    }
    /**
     * 处理滑动播放
     * @param {*} self 
     * @param {*} feed 
     * @param {*} direct 
     * @param {*} i 
     */
    function handleMove(self,feed,direct,i) {
        if(i==self.index){
            detailUtil.setShare(self.feedData);
            if(!feed.playurl){
                feed.playurl = feed.videourl;
                if(!feed.bgmurl_p&&feed.bgmurl){
                    feed.bgmurl_p = feed.bgmurl;
                    self.audio.load();
                }
                self.video.load();
            }
            self.$nextTick(function () {
                store.addPlayNum(feed.shareid);
                if(self.audio&&!self.audioLoaded){
                    var int = setInterval(function () {
                        if(self.audioLoaded){
                            clearInterval(int);
                            self.play(1);
                        }
                    },100);
                }else{
                    self.play(1);
                }
            });
        }
        //direct>0 则是下滑,页面出现上一个视频,则当前位置的下一个视频要暂停,
        //direct<0则是上滑,页面要播放下一个视频了,则当前位置的上一个视频要暂停
        if(self.index == i+(direct>0?1:-1)){
            self.pause();
            if(!self.loadedAll){
                feed.playurl = "";//如果是未加载完成,那么就不要加载了。
                feed.start = 0;
                feed.hide = 1;
            }
            if(!self.audioLoaded){
                feed.bgmurl_p = "";
            }
        }
        if(self.index>=i+7||self.index<=i-7){
            feed.maxHide = 1;//最大超过16个节点,则隐藏。
            feed.playurl = "";//最大超过16个节点,则把其他视频干掉。
            feed.start = 0;
            feed.hide = 1;            
        }else{
            feed.maxHide = 0;
        }
    }
    function loadWX(cb) {
        if(device.scene=="weixin"){
            if(window.WeixinJSBridge){
                cb(1);
            }else{
                document.addEventListener("WeixinJSBridgeReady", function() {
                    cb(1);
                });
            }
        }else{
            cb();
        }
    }
    /**
     * 处理背景
     * @param {*} feed 
     * @param {*} self 
     */
    function handleBGM(feed,self) {
        //背景音乐
        var bgm=feed.bgm;
        if(bgm&&window.bgmlist&&bgmlist[bgm]){
            self.bg_name = bgmlist[bgm].bgmname;
            feed.bgmurl = bgm+".mp3";
            if(self.index==0){
                feed.bgmurl_p = feed.bgmurl;
            }
            self.audio = self.$el.querySelector("audio");
            setPlay(self,0);
            var t = setTimeout(function () {//超时处理
                done();
            },4000);
            loadWX(throuth);
            function throuth(){
                self.audio.addEventListener("canplaythrough",function () {
                    done();
                });
            }
            function done(){
                clearTimeout(t);
                self.vloading = 0;
                self.audioLoaded = 1;
                !feed.start&&(setPlay(self,1));//还没播放,则吧播放按钮弹出来
            }
            
        }else{
            setPlay(self,1);
        }
    }
    function compute(self) {
        if(self.isCd) return;
        var d = self.video.duration;
        if (d>0) {
            self.isCd = true;
            self.duration = d;
            self.checkLoading();
        }
    }
    function setPlay(self,f){
        self.play_btn = f;
        self.vloading = !f;
    }
    function errors(self,msg) {
        if(!self.video||!self.feedData.start) return;
        self.loaderror++;
        if(self.loaderror<=2){
            self.play();
            return;
        }
        setPlay(self,1);
        msg += JSON.stringify(self.video.error)+","+self.video.src;
        store.report(26,0,msg);
    }
           

Uncaught (in promise) DOMException: The element has no supported sources.

1、视频能否正常访问的到,代理了没?

2、是否是标准支持的视频格式,标准的mp4?

下一篇: H5视频video