美文网首页
基于Rxjs的video全功能封装

基于Rxjs的video全功能封装

作者: 是素净呀丶 | 来源:发表于2019-07-18 15:10 被阅读0次

      最近接触到angular项目,有一个播放视频的需求,故尝试用Rxjs实现了该封装。主要实现的功能有:播放控制、进度控制(点击控制条以及拖动进度条)、音量控制(点击控制条以及拖动进度条)、全屏、错误信息展示。
    注: 以下代码中创建的Subscription皆不展示退订代码,实际开发中切记使用unsubscribe进行退订操作。

    • 播放控制

    即暂停时点击按钮播放,播放时点击按钮暂停,核心代码如下:

      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();
            }
          }
        );
      }
    
    • 进度控制 && 音量控制

    这里我把这两个控制操作抽象了progress.service.ts服务,核心代码(Rxjs实现拖拽)如下:

    constructor(options: {
      // 传入控制条元素
      container: Element;
      // 传入控制条拖动元素
      slider?: Element;
      // 拖动方向 (视频控制条左右、音量控制条上下)
      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');
    
      // 通过点击控制条控制进度
      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
        });
      });
    
      // 通过拖动滑块控制进度
      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(
        ...
        ...
        ...
      )
    }
    
    • 全屏控制

    同进度控制,这里抽象为fullscreen.service.ts服务,核心代码如下:

      constructor(options: {
        // 触发全屏的元素 一般为按钮
        trigger: Element;
        // 全屏的目标元素
        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
      });
    }
    
    • 错误信息提示

    即当视频加载出现错误时给用户以提示,视频地址无效,跨域等。之前尝试监听video事件,onerroronabortonstalledonemptied...皆无果,最后解决方案如下:

      get errorTips() {
        if (!this.player) {
          return '';
        }
    
        const { error } = this.player;
        if (!error) {
          return '';
        }
    
        const { code, message } = error;
        return (
          message ||
          ['', '意外的中止', '网络错误', '视频解码错误', '无效的视频地址'][code]
        );
      }
    

    相关文章

      网友评论

          本文标题:基于Rxjs的video全功能封装

          本文链接:https://www.haomeiwen.com/subject/smwklctx.html