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

資訊專(zhuān)欄INFORMATION COLUMN

video.js 源碼分析(JavaScript)

simpleapples / 1710人閱讀

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

video.js 源碼分析(JavaScript)

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

繼承關(guān)系

運(yùn)行機(jī)制

插件的運(yùn)行機(jī)制

插件的定義

插件的運(yùn)行

控制條是如何運(yùn)行的

UI與JavaScript對(duì)象的銜接

類(lèi)的掛載方式

存儲(chǔ)

獲取

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

以下是video.js的源碼組織結(jié)構(gòu)關(guān)系,涉及控制條、菜單、浮層、進(jìn)度條、滑動(dòng)塊、多媒體、音軌字幕、輔助函數(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ū)ο蠓绞絹?lái)實(shí)現(xiàn)的。基類(lèi)是Component,所有其他的類(lèi)都是直接或間接集成此類(lèi)實(shí)現(xiàn)。語(yǔ)法部分采用的是ES6標(biāo)準(zhǔn)。

繼承關(guān)系

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

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

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

運(yùn)行機(jī)制

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

videojs方法源碼如下:

function videojs(id, options, ready) {
let tag;
// id可以是選擇器也可以是DOM節(jié)點(diǎn)
if (typeof id === "string") {
    if (id.indexOf("#") === 0) {
        id = id.slice(1);
    }
    //檢查播放器是否已被實(shí)例化
    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];
    }
    // 如果播放器沒(méi)有實(shí)例化,返回DOM節(jié)點(diǎn)
    tag = Dom.getEl(id);
} else {
    // 如果是DOM節(jié)點(diǎn)直接返回
    tag = id;
}
if (!tag || !tag.nodeName) {
    throw new TypeError("The element or ID supplied is not valid. (videojs)");
}
// 返回播放器實(shí)例
return tag.player || Player.players[tag.playerId] || new Player(tag, options, ready);
}
[]()

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

constructor(tag, options, ready) {
    // 注意這個(gè)tag是video原生標(biāo)簽
    tag.id = tag.id || `vjs_video_${Guid.newGUID()}`;
    // 選項(xiàng)配置的合并
    options = assign(Player.getTagSettings(tag), options);
    // 這個(gè)選項(xiàng)要關(guān)掉否則會(huì)在父類(lèi)自動(dòng)執(zhí)行加載子類(lèi)集合
    options.initChildren = false;
    // 調(diào)用父類(lèi)的createEl方法
    options.createEl = false;
    // 在移動(dòng)端關(guān)掉手勢(shì)動(dòng)作監(jiān)聽(tīng)
    options.reportTouchActivity = false;
    // 檢查播放器的語(yǔ)言配置
    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;
            }
        }
    }
    // 初始化父類(lèi)
    super(null, options, ready);
    // 檢查當(dāng)前對(duì)象必須包含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?");
    }
    // 存儲(chǔ)當(dāng)前已被實(shí)例化的播放器
    this.tag = tag;
    // 存儲(chǔ)video標(biāo)簽的各個(gè)屬性
    this.tagAttributes = tag && Dom.getElAttributes(tag);
    // 將默認(rèn)的英文切換到指定的語(yǔ)言
    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;
    }
    // 緩存各個(gè)播放器的各個(gè)屬性.
    this.cache_ = {};
    // 設(shè)置播放器的貼片
    this.poster_ = options.poster || "";
    // 設(shè)置播放器的控制
    this.controls_ = !!options.controls;
    // 默認(rèn)是關(guān)掉控制
    tag.controls = false;
    this.scrubbing_ = false;
    this.el_ = this.createEl();
    const playerOptionsCopy = mergeOptions(this.options_);
    // 自動(dòng)加載播放器插件
    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);實(shí)例化父類(lèi)Component。我們來(lái)看下Component的構(gòu)造函數(shù):

constructor(player, options, ready) {
    // 之前說(shuō)過(guò)所有的類(lèi)都是繼承Component,不是所有的類(lèi)需要傳player
    if (!player && this.play) {
        // 這里判斷調(diào)用的對(duì)象是不是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) {
        // 這個(gè)initChildren方法是將一個(gè)類(lèi)的子類(lèi)都實(shí)例化,一個(gè)類(lèi)都對(duì)應(yīng)著自己的el(DOM實(shí)例),通過(guò)這個(gè)方法父類(lèi)和子類(lèi)的DOM繼承關(guān)系也就實(shí)現(xiàn)了
        this.initChildren();
    }
    this.ready(ready);
    if (options.reportTouchActivity !== false) {
        this.enableTouchActivity();
    }
}
插件的運(yùn)行機(jī)制 插件的定義
import Player from "./player.js";
// 將插件種植到Player的原型鏈
const plugin = function(name, init) {
  Player.prototype[name] = init;
};
// 暴露plugin接口
videojs.plugin = plugin;
插件的運(yùn)行
// 在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);
}
控制條是如何運(yùn)行的
Player.prototype.options_ = {
  // 此處表示默認(rèn)使用html5的video標(biāo)簽
  techOrder: ["html5", "flash"],
  html5: {},
  flash: {},
  // 默認(rèn)的音量,官方代碼該配置無(wú)效有bug,我們已修復(fù),
  defaultVolume: 0.85,
  // 用戶(hù)的交互時(shí)長(zhǎng),比如超過(guò)這個(gè)時(shí)間表示失去焦點(diǎn)
  inactivityTimeout: 2000,
  playbackRates: [],
  // 這是控制條各個(gè)組成部分,作為Player的子類(lèi)
  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類(lèi)中有個(gè)children配置項(xiàng),這里面是控制條的各個(gè)組成部分的類(lèi)。各個(gè)UI類(lèi)還有子類(lèi),都是通過(guò)children屬性鏈接的。

UI與JavaScript對(duì)象的銜接

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

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

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

這里的tag指的是video標(biāo)簽。

類(lèi)的掛載方式

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

存儲(chǔ)
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里有個(gè)靜態(tài)方法是registerComponet,所有的組件類(lèi)都注冊(cè)到Componet的components_屬性里。

例如控制條類(lèi)ControlBar就是通過(guò)這個(gè)方法注冊(cè)的。

Component.registerComponent("ControlBar", ControlBar);

在Player的children屬性里包括了controlBar類(lèi),然后通過(guò)getComponet獲取這個(gè)類(lèi)。

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

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

如有疑問(wèn),請(qǐng)留言。

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

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

相關(guān)文章

  • video.js 源碼分析JavaScript

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

    Neilyo 評(píng)論0 收藏0
  • video.js 源碼分析JavaScript

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

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

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

    beanlam 評(píng)論0 收藏0
  • 前端插件庫(kù)

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

    高勝山 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<