主頁 > 前端設計 > 記錄--短視頻滑動播放在 H5 下的實作

記錄--短視頻滑動播放在 H5 下的實作

2023-01-04 07:29:05 前端設計

這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

短視頻已經無數不在了,但是主體還是使用 app 來承載的,本文講述 H5 如何實作 app 的視頻滑動體驗,
無聲勝有聲,一圖頂百辯,且看下圖:

網址鏈接(需在微信或者手Q中瀏覽)

從上圖可以看到,我們主要實作的功能也是本文要講解的有:

  • 上滑查看新短視頻
  • 下滑查看歷史短視頻
  • 滑動距離大于一個距離才上翻,否則歸位
  • 滑動有速率的概念,如果速率達到一定值,距離不夠,也會翻頁
  • 自動播放的問題
  • 如何保證當前視頻的播放流暢度

Let's do it ~~~~~~~~~~~~~~~~

本文文字說明可仔細看,很多采坑路上的總結,小程式下滑動視頻的采坑之路下篇跟上,

HTML + CSS 頁面結構

頁面布局采用 ul 布局,如下:

 主要樣式:

.quan_vcd_list-item{
    width:100%;
    height:100vh;
    overflow:hidden;
    position:relative;
    -webkit-overflow-scrolling:touch;
}
.quan_vcd_list.trans{
    transition:transform .3s ease-out 0s,-webkit-transform .3s ease-out 0s;
}
video .video_hide{
    left:-100%;
}

每個item充滿螢屏即可,增加松手時候的影片,

<video
  x5-video-can-play-with-audio="true"
  :
  :src="https://www.cnblogs.com/smileZAZ/p/feedData.playurl"
  preload="auto"
  type="video/mp4"
  
  webkit-playsinline="true"
  playsinline="true"
  loop="loop"
></video>

video的html,其中兩個三個重要點:

  • video需要先要將video移出螢屏外來達到隱藏的目的,如果用display:none的話,用戶點擊可能會出現例外,
  • 設定playsinline,讓視頻在當前頁面播放而不是全屏,
  • x5-video-can-play-with-audio=true,且先執行播放音再播放視頻,如果不設定這個值,那么在微信h5頁面,會出現播放播放了視頻,音頻自動停止的場面,

ul 是一個外層串列,寬度 100%即可,每一個視頻項是一個充滿螢屏的節點,本文不做重點講述了,感興趣可以自行嘗試,

核心js代碼

監聽用戶手勢,動態設定ul的translateY,然后執行音頻和視頻的播放,so easy!

手勢滑動

手勢滑動播放視頻翻頁,主要有兩個條件:滑動距離和滑動加速度,

  1. 初始化 Touch 主要是重置引數,記錄最后的位移,存盤頁面的高度,系結 touch 事件,
function Touch() {
  this.init();
}
Touch.fn = Touch.prototype;
Touch.fn.init = function(params) {
  this.lastY = this.translateY; //記錄當前位移
  this.startY = 0;
  this.moveY = 0;
  this.lock = 0; //如果在回呼或上升階段不允許滑動.
  this.bodyHeight = window.innerHeight;
  this.resetLimitY();
  self.isIOS = /ip(?:hone|ad|od)/.test(navigator.userAgent.toLowerCase());
  ["touchstart", "touchmove", "touchend"].forEach(str => {
    document.addEventListener(this, this[str].bind(this));
  });
};
  1. 監聽手勢的滑動

這里增加了一個 lock 變數,用于當滑動翻頁的時候,頁面處在翻頁中的狀態,需要鎖死滑動,否則會錯亂,
lastY 是上一次滑動完之后的最終狀態距離,translateY 是指滑動中的狀態距離,需要實時變更 ul 的 translateY 的值

檢測滑動的加速度:detectAcc

var a = Math.abs(this.moveY) / (Date.now() - this.start) >= 0.5;

滑動的距離 大于 滑動的時間差的一半,則認為是主動翻頁,需要執行翻頁操作,

這里要注意點:ios 下再滑動程序中,可以立即執行翻頁,體驗很好,但是 android 不行,所以安卓需要等 touchend 之后再執行翻頁

Touch.fn.touchstart = function(e) {
  if (this.lock) return;
  this.startY = e.touches[0].pageY;
  this.start = Date.now(); //標識滑動起始時間,也用于標識滑動start
};
Touch.fn.move = function(y) {
  this.translateY = y;
};
Touch.fn.touchmove = function(e) {
  if (this.lock || !this.start) return; //鎖定了,或者沒有start,主要是手勢一直滑動情況,已經加速度劃走了,手勢需要松開再重新開始
  this.moveY = e.touches[0].pageY - this.startY;
  this.move(this.moveY + this.lastY);
  this.detectAcc();
};
/**
 * 檢測加速度
 */
Touch.fn.detectAcc = function(isEnd) {
  // console.log(self.moveY+"  "+(Date.now()-self.start));
  var a = Math.abs(this.moveY) / (Date.now() - this.start) >= 0.5;
  if (isEnd) {
    if (a) {
      this.limitY = 0;
    }
    this.movend();
    return;
  }
  if (this.isIOS && a) {
    //IOS,可以在touchmove時直接滑動,體驗流暢,
    this.limitY = 0;
    this.movend();
  }
};
  1. 處理翻頁操作

設定翻頁需要滑動的最小距離,這里需要大于 1/3 個螢屏高度,
如果用戶滑動的距離大于最小滑動距離,則翻頁,否則執行歸位,不管歸位還是滑動翻頁,都有一個 transition 的程序,這個程序我們不接受手勢,所以需要等到位移結束之后,才重置 Lock 標識,
這里還用到了動態拉取下下一個視頻(我這里一次拉取兩個視頻),所以需要發出 eventsBus 事件,讓接受該事件拉取新的視頻,

Touch.fn.resetLimitY = function () {
    this.limitY = this.bodyHeight/3;//位移多少才下滑
}
Touch.fn.touchend = function (e) {
    if(this.lock||this.moveY==0||!this.start) return;
    this.detectAcc(1);
}
Touch.fn.movend = function () {
    const self = this;
    this.lock = 1;
    /***
    * 最后上下位移小于最小值則還原為上一次位移,
    * 否則,那么就需要上移或下移一個body寬度,上移則translate加,下移在減去一個body
    * 這里是計算出了應該位移高度,
    */
    let transformY = Math.abs(self.moveY)<self.limitY?self.lastY:self.lastY+self.bodyHeight*(self.moveY>0?1:-1);

    /***
    * 還需計算最大下滑高度和最大上滑高度
    */
    let listUL = document.querySelector(".quan_vcd_list");
    let listHeight = listUL.getBoundingClientRect().height;

    //如果是最后一個li,則不能下滑,
    let maxBottom = (listHeight - self.bodyHeight)*-1;
    let lastComputeY = transformY>0?0:transformY<maxBottom?maxBottom:transformY;
    //停止滑動之后,自動滾動距離,transition
    listUL.classList.add('trans');
    if(lastComputeY<=0){
        let d = lastComputeY-self.lastY;
        d&&events.trigger("touch_move",d,(-lastComputeY/self.bodyHeight));
    }
    self.start = 0;
    (window.requestAnimationFrame|| window.webkitRequestAnimationFrame)(function () {
        self.move(lastComputeY);
        self.moveY = 0;
        self.lastY = lastComputeY;//記錄確定的位置
        if(listHeight + self.lastY<=self.bodyHeight){
            events.trigger("turnPage");
        }
        setTimeout(function () {
            listUL.classList.remove("trans");
            self.lock = 0;
            self.resetLimitY();
        },500);
    });

注冊視頻組件

視頻和背景音兩種控制,如果視頻執行 stalled 和 waiting 連續多次就代表視頻卡住了,需要 show 出加載中的 loading 框,
重點方法:onx5videoexitfullscreen 這個是 x5 瀏覽器在點擊了全屏之后,再回傳頁面的時候,默認是視頻 pause 的,所以需要 play

export default function videoComponent(opt) {
  var config = {
    props: ["feedData", "index"],
    data: function() {
      return {
        play_btn: 0,
        bg_name: "",
        anim_like: [],
        vloading: 0
      };
    },
    mounted:function(){
        this.addEvent();
        this.stall = 0;
        this.loaderror = 0;
    },
    methods: {
      onstalled: function() {
        if (!this.feedData.start) return;
        this.vloading = 1;
        this.play();
        this.stall++;
        if (this.stall == 2) {
          showTip("網路有點慢哦~");
          store.report(27, 1);
        }
      },
      waiting: function() {
        clearInterval(this.timer);
        this.loadTimes = 0;
        this.timer = setInterval(() => {
          this.loadTimes++;
          if (this.loadTimes >= 2) {
            //連續3次未播放,當作是卡住了
            this.aPause();
            this.vloading = 1;
          }
        }, 1800);
      },
      ondurationchange: function() {
        this.compute();
      },
      onl oadedmetadata: function() {
        this.compute();
      },
      ontimeupdate: function() {
        this.timeupdate();
      },
      aPause: function() {
        this.audio && this.audio.pause();
      },
      aPlay: function() {
        this.audio && this.audio.play();
      },
      pause: function() {
        this.video.pause();
        this.aPause();
        this.vloading = 0;
        clearInterval(this.timer);
      },
      play: function(isMove) {
        this.videoPlay(isMove);
      },
      checkLoading: function() {
        checkLoading(this);
      },
      onx5videoexitfullscreen: function() {
        this.video.play();
      }
    }
  };
  Vue.component("video-com", util.assign(config, opt));
}

注冊事件

注冊video的事件,處理背景音樂,監聽上面手勢滑動的翻頁事件,

如果是微信環境,需要等待wx的api加載完成,在android微信里面是無法自動播放視頻,否則可以自動播放視頻,

function addEvent() {
    this.$nextTick( ()=> {
        this.video = this.$el.querySelector("video");
        var arry = ['stalled','playing', 'timeupdate', 'abort', 'error','durationchange','loadedmetadata','pause','x5videoexitfullscreen'];
        arry.forEach( (str)=> {
            this.video.addEventListener(str,this['on'+str]);
        });
        if(this.index==0){
            this.loadWX(function (isWx) {
                if(isWx&&isAndroid) return;
                this.play();
            });
        }
    });
    this.handleBGM(this.feedData);
    let self = this;
    events.listen("touch_move", (direct,i)=> {
        handleMove(self,direct,i);
    });
}
function loadWX(cb) {
    if(device.scene=="weixin"){
        if(window.WeixinJSBridge){
            cb(true);
        }else{
            document.addEventListener("WeixinJSBridgeReady", function() {
                cb(true);
            });
        }
    }else{
        cb();
    }
}

預加載

預加載的前提是當前視頻已經加載完成了,才能預加載下一個視頻,需要標識每一個視頻是否加載完成,

  • 如果當前視頻沒有加載完成,將會把預加載的視頻都干掉,防止占用當前網路
  • 如果當前視頻已經加載完成,則一直預加載下一個,下下一個,對于網路好的尤為明顯,
function checkLoading() {
    let self = this;
    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
    }
}

滑動視頻處理

滑動播放是關鍵,需要保證用戶手勢和執行播放需要是同步的,否則會出現部分手機播放失效,需要多點擊一下播放,

  • 如果是當前視頻,則需要播放當前視頻和音頻
  • 需要停止還沒有下載完的視頻和音頻(停止的做法就是將視頻的url置空即可),否則影響當前播放的順暢度,對于已經下載完的了則不需要處理,
  • 同一個頁面最大存在16個視頻(ios的限制),已當前視頻為中心,上下保留七個視頻,其余視頻全部display:none
function handleMove(self, direct, i) {
  if (i == self.index) {
    handleCurrent(self);
  }
  //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 handleCurrent(self){
    if (!self.feed.playurl) {
      self.feed.playurl = self.feed.videourl;
      if (!self.feed.bgmurl_p && self.feed.bgmurl) {
        self.feed.bgmurl_p = self.feed.bgmurl;
        self.audio.load();
      }
      self.video.load();
    }
    self.$nextTick(()=> {
      store.addPlayNum(feed.shareid);
      if (self.audio && !self.audioLoaded) {
        var int = setInterval(()=> {
          if (self.audioLoaded) {
            clearInterval(int);
            self.play(1);
          }
        }, 100);
      } else {
        self.play(1);
      }
    });
}

播放的Error

播放在不同的手機上可能會進入error(abort,error,stall),我們需要自動再出發一次播放,這個能解決一部分手機二次播放的問題,會無緣無故進入error,導致視頻無法播放,

function errors(msg) {
    let self = this;
    if(!self.video||!self.feedData.start) return;
    self.loaderror++;
    if(self.loaderror<=2){
        self.play();
        return;
    }
    setPlay(1);
    // report msg 
}

本文轉載于:

https://juejin.cn/post/6844903957920219149

如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/541137.html

標籤:HTML5

上一篇:一文讀懂 HTTP/1 HTTP/2 HTTP/3

下一篇:手機端H5 實作自定義拍照界面

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more