国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

video.js 源碼分析(JavaScript)

Neilyo / 2190人閱讀

摘要:語法部分采用的是標準。那么整個播放器是怎么把播放器的加載到中的呢在的構(gòu)造函數(shù)里可以看到先生成,然后初始化父類遍歷屬性,將中的類實例化并將對應(yīng)的嵌入到的屬性中,最后在的構(gòu)造函數(shù)中直接掛載到標簽的父級上。

video.js 源碼分析(JavaScript)

組織結(jié)構(gòu)

繼承關(guān)系

運行機制

插件的運行機制

插件的定義

插件的運行

控制條是如何運行的

UI與JavaScript對象的銜接

類的掛載方式

存儲

獲取

組織結(jié)構(gòu)

以下是video.js的源碼組織結(jié)構(gòu)關(guān)系,涉及控制條、菜單、浮層、進度條、滑動塊、多媒體、音軌字幕、輔助函數(shù)集合等等。

├── control-bar
│?? ├── audio-track-controls
│?? │?? ├── audio-track-button.js
│?? │?? └── audio-track-menu-item.js
│?? ├── playback-rate-menu
│?? │?? ├── playback-rate-menu-button.js
│?? │?? └── playback-rate-menu-item.js
│?? ├── progress-control
│?? │?? ├── load-progress-bar.js
│?? │?? ├── mouse-time-display.js
│?? │?? ├── play-progress-bar.js
│?? │?? ├── progress-control.js
│?? │?? ├── seek-bar.js
│?? │?? └── tooltip-progress-bar.js
│?? ├── spacer-controls
│?? │?? ├── custom-control-spacer.js
│?? │?? └── spacer.js
│?? ├── text-track-controls
│?? │?? ├── caption-settings-menu-item.js
│?? │?? ├── captions-button.js
│?? │?? ├── chapters-button.js
│?? │?? ├── chapters-track-menu-item.js
│?? │?? ├── descriptions-button.js
│?? │?? ├── off-text-track-menu-item.js
│?? │?? ├── subtitles-button.js
│?? │?? ├── text-track-button.js
│?? │?? └── text-track-menu-item.js
│?? ├── time-controls
│?? │?? ├── current-time-display.js
│?? │?? ├── duration-display.js
│?? │?? ├── remaining-time-display.js
│?? │?? └── time-divider.js
│?? ├── volume-control
│?? │?? ├── volume-bar.js
│?? │?? ├── volume-control.js
│?? │?? └── volume-level.js
│?? ├── control-bar.js
│?? ├── fullscreen-toggle.js
│?? ├── live-display.js
│?? ├── mute-toggle.js
│?? ├── play-toggle.js
│?? ├── track-button.js
│?? └── volume-menu-button.js
├── menu
│?? ├── menu-button.js
│?? ├── menu-item.js
│?? └── menu.js
├── popup
│?? ├── popup-button.js
│?? └── popup.js
├── progress-bar
│?? ├── progress-control
│?? │?? ├── load-progress-bar.js
│?? │?? ├── mouse-time-display.js
│?? │?? ├── play-progress-bar.js
│?? │?? ├── progress-control.js
│?? │?? ├── seek-bar.js
│?? │?? └── tooltip-progress-bar.js
│?? └── progress-bar.js
├── slider
│?? └── slider.js
├── tech
│?? ├── flash-rtmp.js
│?? ├── flash.js
│?? ├── html5.js
│?? ├── loader.js
│?? └── tech.js
├── tracks
│?? ├── audio-track-list.js
│?? ├── audio-track.js
│?? ├── html-track-element-list.js
│?? ├── html-track-element.js
│?? ├── text-track-cue-list.js
│?? ├── text-track-display.js
│?? ├── text-track-list-converter.js
│?? ├── text-track-list.js
│?? ├── text-track-settings.js
│?? ├── text-track.js
│?? ├── track-enums.js
│?? ├── track-list.js
│?? ├── track.js
│?? ├── video-track-list.js
│?? └── video-track.js
├── utils
│?? ├── browser.js
│?? ├── buffer.js
│?? ├── dom.js
│?? ├── events.js
│?? ├── fn.js
│?? ├── format-time.js
│?? ├── guid.js
│?? ├── log.js
│?? ├── merge-options.js
│?? ├── stylesheet.js
│?? ├── time-ranges.js
│?? ├── to-title-case.js
│?? └── url.js
├── big-play-button.js
├── button.js
├── clickable-component.js
├── close-button.js
├── component.js
├── error-display.js
├── event-target.js
├── extend.js
├── fullscreen-api.js
├── loading-spinner.js
├── media-error.js
├── modal-dialog.js
├── player.js
├── plugins.js
├── poster-image.js
├── setup.js
└── video.js

video.js的JavaScript部分都是采用面向?qū)ο蠓绞絹韺崿F(xiàn)的。基類是Component,所有其他的類都是直接或間接集成此類實現(xiàn)。語法部分采用的是ES6標準。

繼承關(guān)系

深入源碼解讀需要了解類與類之間的繼承關(guān)系,直接上圖。

所有的繼承關(guān)系

主要的繼承關(guān)系

運行機制

首先調(diào)用videojs啟動播放器,videojs方法判斷當前id是否已被實例化,如果沒有實例化新建一個Player對象,因Player繼承Component會自動初始化Component類。如果已經(jīng)實例化直接返回Player對象。

videojs方法源碼如下:

function videojs(id, options, ready) {
let tag;
// id可以是選擇器也可以是DOM節(jié)點
if (typeof id === "string") {
    if (id.indexOf("#") === 0) {
        id = id.slice(1);
    }
    //檢查播放器是否已被實例化
    if (videojs.getPlayers()[id]) {
        if (options) {
            log.warn(`Player "${id}" is already initialised. Options will not be applied.`);
        }
        if (ready) {
            videojs.getPlayers()[id].ready(ready);
        }
        return videojs.getPlayers()[id];
    }
    // 如果播放器沒有實例化,返回DOM節(jié)點
    tag = Dom.getEl(id);
} else {
    // 如果是DOM節(jié)點直接返回
    tag = id;
}
if (!tag || !tag.nodeName) {
    throw new TypeError("The element or ID supplied is not valid. (videojs)");
}
// 返回播放器實例
return tag.player || Player.players[tag.playerId] || new Player(tag, options, ready);
}
[]()

接下來我們看下Player的構(gòu)造函數(shù),代碼如下:

constructor(tag, options, ready) {
    // 注意這個tag是video原生標簽
    tag.id = tag.id || `vjs_video_${Guid.newGUID()}`;
    // 選項配置的合并
    options = assign(Player.getTagSettings(tag), options);
    // 這個選項要關(guān)掉否則會在父類自動執(zhí)行加載子類集合
    options.initChildren = false;
    // 調(diào)用父類的createEl方法
    options.createEl = false;
    // 在移動端關(guān)掉手勢動作監(jiān)聽
    options.reportTouchActivity = false;
    // 檢查播放器的語言配置
    if (!options.language) {
        if (typeof tag.closest === "function") {
            const closest = tag.closest("[lang]");
            if (closest) {
                options.language = closest.getAttribute("lang");
            }
        } else {
            let element = tag;
            while (element && element.nodeType === 1) {
                if (Dom.getElAttributes(element).hasOwnProperty("lang")) {
                    options.language = element.getAttribute("lang");
                    break;
                }
                element = element.parentNode;
            }
        }
    }
    // 初始化父類
    super(null, options, ready);
    // 檢查當前對象必須包含techOrder參數(shù)
    if (!this.options_ || !this.options_.techOrder || !this.options_.techOrder.length) {
        throw new Error("No techOrder specified. Did you overwrite " +
            "videojs.options instead of just changing the " +
            "properties you want to override?");
    }
    // 存儲當前已被實例化的播放器
    this.tag = tag;
    // 存儲video標簽的各個屬性
    this.tagAttributes = tag && Dom.getElAttributes(tag);
    // 將默認的英文切換到指定的語言
    this.language(this.options_.language);
    if (options.languages) {
        const languagesToLower = {};
        Object.getOwnPropertyNames(options.languages).forEach(function(name) {
            languagesToLower[name.toLowerCase()] = options.languages[name];
        });
        this.languages_ = languagesToLower;
    } else {
        this.languages_ = Player.prototype.options_.languages;
    }
    // 緩存各個播放器的各個屬性.
    this.cache_ = {};
    // 設(shè)置播放器的貼片
    this.poster_ = options.poster || "";
    // 設(shè)置播放器的控制
    this.controls_ = !!options.controls;
    // 默認是關(guān)掉控制
    tag.controls = false;
    this.scrubbing_ = false;
    this.el_ = this.createEl();
    const playerOptionsCopy = mergeOptions(this.options_);
    // 自動加載播放器插件
    if (options.plugins) {
        const plugins = options.plugins;
        Object.getOwnPropertyNames(plugins).forEach(function(name) {
            if (typeof this[name] === "function") {
                this[name](plugins[name]);
            } else {
                log.error("Unable to find plugin:", name);
            }
        }, this);
    }
    this.options_.playerOptions = playerOptionsCopy;
    this.initChildren();
    // 判斷是不是音頻
    this.isAudio(tag.nodeName.toLowerCase() === "audio");
    if (this.controls()) {
        this.addClass("vjs-controls-enabled");
    } else {
        this.addClass("vjs-controls-disabled");
    }
    this.el_.setAttribute("role", "region");
    if (this.isAudio()) {
        this.el_.setAttribute("aria-label", "audio player");
    } else {
        this.el_.setAttribute("aria-label", "video player");
    }
    if (this.isAudio()) {
        this.addClass("vjs-audio");
    }
    if (this.flexNotSupported_()) {
        this.addClass("vjs-no-flex");
    }

    if (!browser.IS_IOS) {
        this.addClass("vjs-workinghover");
    }
    Player.players[this.id_] = this;
    this.userActive(true);
    this.reportUserActivity();
    this.listenForUserActivity_();
    this.on("fullscreenchange", this.handleFullscreenChange_);
    this.on("stageclick", this.handleStageClick_);
}

在Player的構(gòu)造器中有一句super(null, options, ready);實例化父類Component。我們來看下Component的構(gòu)造函數(shù):

constructor(player, options, ready) {
    // 之前說過所有的類都是繼承Component,不是所有的類需要傳player
    if (!player && this.play) {
        // 這里判斷調(diào)用的對象是不是Player本身,是本身只需要返回自己
        this.player_ = player = this; // eslint-disable-line
    } else {
        this.player_ = player;
    }
    this.options_ = mergeOptions({}, this.options_);
    options = this.options_ = mergeOptions(this.options_, options);
    this.id_ = options.id || (options.el && options.el.id);
    if (!this.id_) {
        const id = player && player.id && player.id() || "no_player";
        this.id_ = `${id}_component_${Guid.newGUID()}`;
    }
    this.name_ = options.name || null;
    if (options.el) {
        this.el_ = options.el;
    } else if (options.createEl !== false) {
        this.el_ = this.createEl();
    }
    this.children_ = [];
    this.childIndex_ = {};
    this.childNameIndex_ = {};
    // 知道Player的構(gòu)造函數(shù)為啥要設(shè)置initChildren為false了吧
    if (options.initChildren !== false) {
        // 這個initChildren方法是將一個類的子類都實例化,一個類都對應(yīng)著自己的el(DOM實例),通過這個方法父類和子類的DOM繼承關(guān)系也就實現(xiàn)了
        this.initChildren();
    }
    this.ready(ready);
    if (options.reportTouchActivity !== false) {
        this.enableTouchActivity();
    }
}
插件的運行機制 插件的定義
import Player from "./player.js";
// 將插件種植到Player的原型鏈
const plugin = function(name, init) {
  Player.prototype[name] = init;
};
// 暴露plugin接口
videojs.plugin = plugin;
插件的運行
// 在Player的構(gòu)造函數(shù)里判斷是否使用了插件,如果有遍歷執(zhí)行
if (options.plugins) {
    const plugins = options.plugins;
    Object.getOwnPropertyNames(plugins).forEach(function(name) {
    if (typeof this[name] === "function") {
        this[name](plugins[name]);
    } else {
        log.error("Unable to find plugin:", name);
    }
    }, this);
}
控制條是如何運行的
Player.prototype.options_ = {
  // 此處表示默認使用html5的video標簽
  techOrder: ["html5", "flash"],
  html5: {},
  flash: {},
  // 默認的音量,官方代碼該配置無效有bug,我們已修復(fù),
  defaultVolume: 0.85,
  // 用戶的交互時長,比如超過這個時間表示失去焦點
  inactivityTimeout: 2000,
  playbackRates: [],
  // 這是控制條各個組成部分,作為Player的子類
  children: [
    "mediaLoader",
    "posterImage",
    "textTrackDisplay",
    "loadingSpinner",
    "bigPlayButton",
    "progressBar",
    "controlBar",
    "errorDisplay",
    "textTrackSettings"
  ],
  language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || "en",
  languages: {},
  notSupportedMessage: "No compatible source was found for this media."
};

Player類中有個children配置項,這里面是控制條的各個組成部分的類。各個UI類還有子類,都是通過children屬性鏈接的。

UI與JavaScript對象的銜接

video.js里都是組件化實現(xiàn)的,小到一個按鈕大到一個播放器都是一個繼承了Component類的對象實例,每個對象包含一個el屬性,這個el對應(yīng)一個DOM實例,el是通過createEl生成的DOM實例,在Component基類中包含一個方法createEl方法,子類也可以重寫該方法。類與類的從屬關(guān)系是通過children屬性連接。

那么整個播放器是怎么把播放器的UI加載到HTML中的呢?在Player的構(gòu)造函數(shù)里可以看到先生成el,然后初始化父類遍歷Children屬性,將children中的類實例化并將對應(yīng)的DOM嵌入到player的el屬性中,最后在Player的構(gòu)造函數(shù)中直接掛載到video標簽的父級DOM上。

if (tag.parentNode) {
  tag.parentNode.insertBefore(el, tag);
}

這里的tag指的是video標簽。

類的掛載方式

上文有提到過UI的從屬關(guān)系是通過類的children方法連接的,但是所有的類都是關(guān)在Component類上的。這主要是基于對模塊化的考慮,通過這種方式實現(xiàn)了模塊之間的通信。

存儲
static registerComponent(name, comp) {
    if (!Component.components_) {
      Component.components_ = {};
    }

    Component.components_[name] = comp;
    return comp;
}
獲取
static getComponent(name) {
    if (Component.components_ && Component.components_[name]) {
      return Component.components_[name];
    }

    if (window && window.videojs && window.videojs[name]) {
      log.warn(`The ${name} component was added to the videojs object when it should be registered using videojs.registerComponent(name, component)`);
      return window.videojs[name];
    }
}

在Componet里有個靜態(tài)方法是registerComponet,所有的組件類都注冊到Componet的components_屬性里。

例如控制條類ControlBar就是通過這個方法注冊的。

Component.registerComponent("ControlBar", ControlBar);

在Player的children屬性里包括了controlBar類,然后通過getComponet獲取這個類。

.filter((child) => {
  const c = Component.getComponent(child.opts.componentClass ||
                                 toTitleCase(child.name));

  return c && !Tech.isTech(c);
})

如有疑問,請留言。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/111624.html

相關(guān)文章

  • video.js 源碼分析JavaScript

    摘要:語法部分采用的是標準。那么整個播放器是怎么把播放器的加載到中的呢在的構(gòu)造函數(shù)里可以看到先生成,然后初始化父類遍歷屬性,將中的類實例化并將對應(yīng)的嵌入到的屬性中,最后在的構(gòu)造函數(shù)中直接掛載到標簽的父級上。 video.js 源碼分析(JavaScript) 組織結(jié)構(gòu) 繼承關(guān)系 運行機制 插件的運行機制 插件的定義 插件的運行 控制條是如何運行的 UI與JavaScript對象的...

    simpleapples 評論0 收藏0
  • video.js 源碼分析JavaScript

    摘要:語法部分采用的是標準。那么整個播放器是怎么把播放器的加載到中的呢在的構(gòu)造函數(shù)里可以看到先生成,然后初始化父類遍歷屬性,將中的類實例化并將對應(yīng)的嵌入到的屬性中,最后在的構(gòu)造函數(shù)中直接掛載到標簽的父級上。 video.js 源碼分析(JavaScript) 組織結(jié)構(gòu) 繼承關(guān)系 運行機制 插件的運行機制 插件的定義 插件的運行 控制條是如何運行的 UI與JavaScript對象的...

    SnaiLiu 評論0 收藏0
  • 基于 Node+express 爬蟲的數(shù)據(jù) API,爬一套自己的api數(shù)據(jù)(2)

    摘要:目前半島局勢緊張,朝鮮已進行了六次核試驗,被廣泛認為已經(jīng)擁有了核彈頭。另外朝鮮的導彈技術(shù)今年以來快速突破,成功試射了射程可覆蓋美國本土的洲際彈道導彈。這個版的內(nèi)容傳到互聯(lián)網(wǎng)上后,迅速刷屏,引起紛紛議論。 SplderApi2 Node-SplderApi2 第二版 基于Node 的網(wǎng)絡(luò)爬蟲 API接口 包括前端開發(fā)日報、kugou音樂、前端top框架排行、妹紙福利、搞笑視頻、段子笑話、...

    beanlam 評論0 收藏0
  • 前端插件庫

    摘要:原文鏈接前端插件庫站點前端開發(fā)文檔博客前端插件庫前端插件庫官網(wǎng)是的函數(shù)庫,目的是強化表格操作如搜索排序,并自動加入組件引入表格中,使用非常靈活簡便。由推出,靈活扎實的建議列表函數(shù)庫。 原文鏈接:前端插件庫站點:前端開發(fā)文檔博客:前端插件庫 前端插件庫 DataTables 官網(wǎng):https://www.datatables.net/ DataTables是jQuery的JavaScr...

    高勝山 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<