基于Rxjs的video全功能封裝

??最近接觸到angular項(xiàng)目椒涯,有一個(gè)播放視頻的需求视搏,故嘗試用Rxjs實(shí)現(xiàn)了該封裝径密。主要實(shí)現(xiàn)的功能有:播放控制午阵、進(jìn)度控制(點(diǎn)擊控制條以及拖動(dòng)進(jìn)度條)、音量控制(點(diǎn)擊控制條以及拖動(dòng)進(jìn)度條)享扔、全屏趟庄、錯(cuò)誤信息展示。
注: 以下代碼中創(chuàng)建的Subscription皆不展示退訂代碼伪很,實(shí)際開(kāi)發(fā)中切記使用unsubscribe進(jìn)行退訂操作戚啥。

  • 播放控制

即暫停時(shí)點(diǎn)擊按鈕播放,播放時(shí)點(diǎn)擊按鈕暫停锉试,核心代碼如下:

  get isplaying(): boolean {
      return this.player && !this.player.paused;
  }

  initPlayOrPauseEvent():void {
    this.playOrPause$ = fromEvent(this.doms.playOrPause, 'click').subscribe(
      () => {
        if (!this.canOperate) {
          return;
        }

        if (this.isplaying) {
          this.pause();
        } else {
          this.play();
        }
      }
    );
  }
  • 進(jìn)度控制 && 音量控制

這里我把這兩個(gè)控制操作抽象了progress.service.ts服務(wù)猫十,核心代碼(Rxjs實(shí)現(xiàn)拖拽)如下:

constructor(options: {
  // 傳入控制條元素
  container: Element;
  // 傳入控制條拖動(dòng)元素
  slider?: Element;
  // 拖動(dòng)方向 (視頻控制條左右、音量控制條上下)
  direction?: number;
}) {
  ...
  ...
  ...
  this.init();
}

init() {
  this.mouseClick$ = fromEvent(this.$container, 'click');
  this.mouseDown$ = fromEvent(this.$slider, 'mousedown');
  this.mouseMove$ = fromEvent(document.body, 'mousemove');
  this.mouseUp$ = fromEvent(document.body, 'mouseup');

  // 通過(guò)點(diǎn)擊控制條控制進(jìn)度
  this.mouseClickSub$ = this.mouseClick$.subscribe((event: MouseEvent) => {
    const { direction } = this;
    const { clientX, clientY } = event;
    const bounds = this.$container.getBoundingClientRect();
    const { left, bottom, width, height } = bounds;
    let progress;

    if (direction === 0) {
      progress = ((clientX - left) / width) * 100;
    }

    if (direction === 1) {
      progress = ((bottom - clientY) / height) * 100;
    }

    progress = Math.min(100, Math.max(0, progress));

    this.$emit(PROGRESS_EVENT.CHANGE_END, {
      type: 'click',
      progress,
      event
    });
  });

  // 通過(guò)拖動(dòng)滑塊控制進(jìn)度
  this.dragSub$ = this.mouseDown$
    .pipe(
      tap((e: MouseEvent) => {
        const { clientX, clientY } = e;

        this.initPos = {
          left: this.getStyle(this.$slider, 'left'),
          top: this.getStyle(this.$slider, 'top')
        };
        this.initMousePos = {
          x: clientX,
          y: clientY
        };

        this._draging = true;
        this.$emit(PROGRESS_EVENT.CHANGE_START);
      }),

      switchMap(_ =>
        this.mouseMove$.pipe(
          takeUntil(
            this.mouseUp$.pipe(
              tap(_ => {
                this._draging = false;
                this.$emit(PROGRESS_EVENT.CHANGE_END);
              })
            )
          ),
        map((e: MouseEvent) => {
            const { clientX, clientY } = e;

            return {
              x: clientX - this.initMousePos.x,
              y: clientY - this.initMousePos.y
            };
          })
        )
      )
    )
    .subscribe(movedPos => {
      const targetPos = {
        left: this.initPos.left + movedPos.x,
        top: this.initPos.top + movedPos.y
      };
      const { clientWidth, clientHeight } = this.$container;
      const {
        clientWidth: clientWidthSlider,
        clientHeight: clientHeightSlider
      } = this.$slider;

      targetPos.left = Math.min(
        clientWidth - clientWidthSlider / 2,
        Math.max(-clientWidthSlider / 2, targetPos.left)
      );
      targetPos.top = Math.min(
        clientHeight - clientHeightSlider / 2,
        Math.max(-clientHeightSlider / 2, targetPos.top)
      );

      let progress: number;
      if (this.direction === 0) {
        progress =
          ((targetPos.left + clientWidthSlider / 2) / clientWidth) * 100;
      } else {
        progress =
          ((targetPos.top + clientHeightSlider / 2) / clientHeight) * 100;
        progress = 100 - progress;
      }
      progress = Math.min(100, Math.max(0, progress));

      this.$emit(PROGRESS_EVENT.CHANGING, {
        type: 'drag',
        progress
      });
    });
}

使用:

initEvents() {
  this.videoProgress = new ProgressService({
    container: this.doms.play_progess
  }).$on(PROGRESS_EVENT.CHANGE_START, () => {})
    .$on(PROGRESS_EVENT.CHANGING, () => [})
    .$on(PROGRESS_EVENT.CHANGE_END, () => {});

  this.soundProgress = new ProgressService({
    container: this.doms.sound_progress,
    direction: 1
  }).$on(
    ...
    ...
    ...
  )
}
  • 全屏控制

同進(jìn)度控制呆盖,這里抽象為fullscreen.service.ts服務(wù)拖云,核心代碼如下:

  constructor(options: {
    // 觸發(fā)全屏的元素 一般為按鈕
    trigger: Element;
    // 全屏的目標(biāo)元素
    target: Element;
   }
  ) {
    ...
    ...
    ...
    this.init();
  }

  hasFullscreen() {
    const element = document as any;

    return !!(
      element.isFullScreen ||
      element.mozIsFullScreen ||
      element.msIsFullScreen ||
      element.webkitIsFullScreen ||
      element.fullScreenElement ||
      element.msFullscreenElement ||
      element.mozFullScreenElement ||
      element.webkitFullscreenElement
    );
  }

  fullScreen() {
    const element = this.$target as any;

    if (element.requestFullscreen) {
      element.requestFullscreen();
    } else if (element.msRequestFullscreen) {
      element.msRequestFullscreen();
    } else if (element.mozRequestFullScreen) {
      element.mozRequestFullScreen();
    } else if (element.webkitRequestFullscreen) {
      element.webkitRequestFullscreen();
    }
  }

  exitFullscreen() {
    const element = document as any;

    if (element.exitFullscreen) {
      element.exitFullscreen();
    } else if ((element as any).msExitFullscreen) {
      (element as any).msExitFullscreen();
    } else if ((element as any).mozCancelFullScreen) {
      (element as any).mozCancelFullScreen();
    } else if ((element as any).webkitExitFullscreen) {
      (element as any).webkitExitFullscreen();
    }
  }

  init() {
    this.triggerClick$ = fromEvent(this.$trigger, 'click').subscribe(() => {
      if (this.hasFullscreen()) {
        this.exitFullscreen();
      } else {
        this.fullScreen();
      }

      this.$emit(FULLSCREEN_EVENTS.CHANGE, this.hasFullscreen());
    });
  }

使用:

 initQuanpinEvent() {
  this.fullscreenService = new FullscreenService({
    trigger: this.doms.quanpin,
    target: this.doms.container
  });
}
  • 錯(cuò)誤信息提示

即當(dāng)視頻加載出現(xiàn)錯(cuò)誤時(shí)給用戶以提示,視頻地址無(wú)效应又,跨域等宙项。之前嘗試監(jiān)聽(tīng)video事件,onerror株扛、onabort尤筐、onstalledonemptied...皆無(wú)果洞就,最后解決方案如下:

  get errorTips() {
    if (!this.player) {
      return '';
    }

    const { error } = this.player;
    if (!error) {
      return '';
    }

    const { code, message } = error;
    return (
      message ||
      ['', '意外的中止', '網(wǎng)絡(luò)錯(cuò)誤', '視頻解碼錯(cuò)誤', '無(wú)效的視頻地址'][code]
    );
  }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盆繁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子旬蟋,更是在濱河造成了極大的恐慌油昂,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倾贰,死亡現(xiàn)場(chǎng)離奇詭異冕碟,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)匆浙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門安寺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吞彤,你說(shuō)我怎么就攤上這事我衬。” “怎么了饰恕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵挠羔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我埋嵌,道長(zhǎng)破加,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任雹嗦,我火速辦了婚禮范舀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘了罪。我一直安慰自己锭环,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布泊藕。 她就那樣靜靜地躺著辅辩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪娃圆。 梳的紋絲不亂的頭發(fā)上玫锋,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音讼呢,去河邊找鬼撩鹿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛悦屏,可吹牛的內(nèi)容都是我干的节沦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼础爬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼散劫!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起幕帆,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤获搏,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后失乾,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體常熙,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年碱茁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了裸卫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纽竣,死狀恐怖墓贿,靈堂內(nèi)的尸體忽然破棺而出茧泪,到底是詐尸還是另有隱情,我是刑警寧澤聋袋,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布队伟,位于F島的核電站,受9級(jí)特大地震影響幽勒,放射性物質(zhì)發(fā)生泄漏嗜侮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一啥容、第九天 我趴在偏房一處隱蔽的房頂上張望锈颗。 院中可真熱鬧,春花似錦咪惠、人聲如沸击吱。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)姨拥。三九已至,卻和暖如春渠鸽,著一層夾襖步出監(jiān)牢的瞬間叫乌,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工徽缚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留憨奸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓凿试,卻偏偏與公主長(zhǎng)得像排宰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子那婉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容