如何實現沉浸式視頻體驗?

沉浸式視頻體驗

上週產品小哥哥丟過來一個需求,名曰:沉浸式視頻體驗,大體內容是一個頁面裏有幾十個視頻,用戶點擊其中一個視頻時,該視頻自動滑動到屏幕可視區域的頂部開始播放,並暫停其餘視頻,該視頻滑出屏幕可視區域以後要自動暫停。html

這個需求有兩個關鍵的技術點:前端

  1. 如何將視頻滑動到屏幕可視區域的頂部
  2. 如何判斷視頻滑出了屏幕可視區域

其實這兩個技術點有一個共同點,就是需求計算出元素在頁面中的絕對位置,也就是指該元素的左上角相對於整張網頁左上角的座標,有兩種方法能夠計算獲得:git

1.遞歸

利用offsetTopoffsetLeft能夠取到當前元素左上角相對於其HTMLElement.offsetParent節點的左邊界偏移的像素值,而後再利用HTMLElement.offsetParent
能夠獲得一個指向最近的包含該元素的定位元素。github

若是沒有定位的元素,則offsetParent爲最近的table,table cell或根元素(標準模式下爲htmlquirks模式下爲body)。web

利用以上三個屬性,寫一個遞歸函數就能夠獲得當前元素在頁面中的絕對位置了:算法

const getElementLeft = element => {
    let actualLeft = element.offsetLeft;
    let current = element.offsetParent;
    while (current !== null){
        actualLeft += current.offsetLeft;
        current = current.offsetParent;
    }
    return actualLeft;
}
const getElementTop = element => {
    let actualTop = element.offsetTop;
    let current = element.offsetParent;
    while (current !== null){
        actualTop += current.offsetTop;
        current = current.offsetParent;
    }
    return actualTop;
}

注意:因爲在表格和iframe中,offsetParent對象未必等於父容器,因此上面的函數對於表格和iframe中的元素不適用。小程序

2. 你據說過getBoundingClientRect嗎?

object.getBoundingClientRect()的返回值包含了一組只讀屬性,包括該元素相對於視口左上角位置的lefttoprightbottom,以及元素的widthheight,單位爲像素,具體含義可參見下圖:
segmentfault

從上圖能夠看出,getBoundingClientRect獲得的值是相對於視口的,而不是絕對的,當視口區域或其餘可滾動元素內發生滾動操做時,top和left屬性值就會隨之當即發生變化。dom

要得到相對於整個網頁左上角定位的屬性值,只要給top、left屬性值加上當前的滾動位置(經過window.scrollXwindow.scrollY),這樣就能夠獲取與當前的滾動位置無關的值。函數

如何將視頻滑動到屏幕可視區域的頂部?

考慮到getBoundingClientRect的兼容性較好,且算法複雜度較低,最終我採用了getBoundingClientRect方法來實現「將視頻滑動到屏幕可視區域的頂部」的功能,使用window.scroll來實現頁面滾動,使用setTimeout增長滾動時的動畫,具體實現滾動的函數以下:

const autoScroll = (offsetTop, needScrollTop, hasScrollTop) => {
    let _needScrollTop = needScrollTop; // 本次遞歸時,離終點的距離
    let _hasScrollTop = hasScrollTop; // 本次遞歸時,已經移動的距離總和
    const speed = 10;
    setTimeout(() => {
        const dist = needScrollTop > 0
            ? Math.max(Math.ceil(needScrollTop / speed), 5)
            : Math.min(Math.ceil(needScrollTop / speed), -5);
        _needScrollTop -= dist;
        _hasScrollTop += dist;
        window.scroll(0, offsetTop + _hasScrollTop);
        // 若是移動幅度小於十個像素,直接移動,不然遞歸調用,實現動畫效果
        if (_needScrollTop > speed || _needScrollTop < -speed) {
            this.__onScroll(offsetTop, _needScrollTop, _hasScrollTop);
        } else {
            window.scroll(0, offsetTop + _hasScrollTop + _needScrollTop);
        }
    }, 1);
}

const rect = element.getBoundingClientRect();

const offsetTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || window.screenY;

autoScroll(offsetTop, rect.top, 0);

如何判斷視頻滑出了屏幕可視區域?

利用getBoundingClientRect的返回值一樣能夠判斷元素在屏幕可視區域的曝光和滑出,經過監聽頁面的滾動事件,在滾動結束時,觸發checkLeavecheckExpose函數,具體代碼以下:

// 檢測滑出可視區域
checkLeave() {
    const element = document.querySelector(`[data-action-id="${this.__domId}"]`);
    if (!element) {
        console.error(`Action: element [data-action-id="${this.__domId}"] not found`);
        return;
    }
    const { top, bottom, left, right } = element.getBoundingClientRect();
    if ((top > getWindowHeight() || bottom < 0
        || left > getWindowWidth() || right < 0)) {
        this.onLeave(); //onLeave函數中實現具體業務邏輯
    }
}

// 檢測真實曝光
checkExpose() {
    const element = document.querySelector(`[data-action-id="${this.__domId}"]`);
    if (!element) {
        console.error(`Action: element [data-action-id="${this.__domId}"] not found`);
        return;
    }
    const { top, bottom, left, right } = element.getBoundingClientRect();
    if (Math.max(0, top) <= Math.min(getWindowHeight(), bottom)
        && Math.max(0, left) <= Math.min(getWindowWidth(), right)) {
        this.onExpose(); //onExpose函數中實現具體業務邏輯
    }
}

能夠將這兩個事件封裝成了一個<Action>組件的兩個props參數,使用時只須要在須要的元素外包一層<Action>父元素,並傳入特定的回調函數便可。

BTW,多說一句我實現document.querySelector的原理,直接看代碼:

render() {
    const { children } = this.props;
    return cloneElement(Children.only(children), {
        'data-action-id': this.__domId,
    });
}

以上。

做者:TNFE 二小

團隊推廣

TNFE團隊爲前端開發人員整理出了小程序以及web前端技術領域的最新優質內容,每週更新✨,歡迎star,github地址:https://github.com/Tnfe/TNFE-Weekly歡迎加入騰訊前端技術交流QQ羣:784383520

相關文章
相關標籤/搜索