javascript - 如何使用angular创建可重用的音频播放器组件

标签 javascript html angular typescript audio

我试图创建一个可重复使用的音频播放器组件,以便在整个项目中使用。我遵循了这个tutorial来创建音频播放器本身。
现在的问题是,我决定让音频播放器自己成为一个可以在其他组件中呈现的组件,然后使用@Input传递音频URL。它可以与该组件的一个实例正常工作,但是一旦创建了该组件的另一个实例,该组件的两个渲染实例的行为相同,它们播放相同的文件,一个组件上的按钮会影响另一个组件,即,如果我按一个音频播放器上的播放按钮,第二个也将使用相同的文件播放。
这是相关的代码。
audio.service.ts

import { Injectable } from '@angular/core';
import { Subject, Observable, BehaviorSubject } from 'rxjs';
import { StreamState } from './stream-state';
import { takeUntil } from 'rxjs/operators';
import * as moment from 'moment';

@Injectable({
  providedIn: 'root',
})
export class AudioService {
  constructor() {}

  private stop$ = new Subject();
  private audioObject: HTMLAudioElement = new Audio();

  private audioEvents: Array<string> = [
    'ended',
    'error',
    'play',
    'playing',
    'pause',
    'timeupdate',
    'canplay',
    'loadedmetadata',
    'loadstart',
  ];
  private state: StreamState = {
    playing: false,
    readableCurrentTime: '',
    readableDuration: '',
    duration: undefined,
    currentTime: undefined,
    canplay: false,
    error: false,
    mute: false,
  };
  private streamObservable(url) {
    return new Observable(observer => {
      // Play audio
      this.audioObject.src = url;
      this.audioObject.load();
      this.audioObject.play();

      const handler = (event: Event) => {
        this.updateStateEvents(event);
        observer.next(event);
      };
      this.addEvents(this.audioObject, this.audioEvents, handler);
      return () => {
        this.audioObject.pause();
        this.audioObject.currentTime = 0;

        this.removeEvent(this.audioObject, this.audioEvents, handler);
        this.resetState();
      };
    });
  }
  private addEvents(obj, events, handler) {
    events.forEach(event => {
      obj.addEventListener(event, handler);
    });
  }

  private removeEvent(obj, events, handler) {
    events.forEach(event => {
      obj.removeEventListener(event, handler);
    });
  }

  playStream(url) {
    return this.streamObservable(url).pipe(takeUntil(this.stop$));
  }

  play() {
    this.audioObject.play();
  }
  pause() {
    this.audioObject.pause();
  }
  stop() {
    this.stop$.next();
  }
  mute() {
    this.audioObject.volume = 0;
    this.state.mute = true;
  }
  unmute() {
    this.audioObject.volume = 1;
    this.state.mute = false;
  }
  seekTo(seconds) {
    this.audioObject.currentTime = seconds;
  }

  formatTime(time: number, format: string = 'mm:ss') {
    const momentTime = time * 1000;
    return moment.utc(momentTime).format(format);
  }

  private stateChange: BehaviorSubject<StreamState> = new BehaviorSubject(this.state);

  private updateStateEvents(event: Event): void {
    switch (event.type) {
      case 'canplay':
        this.state.duration = this.audioObject.duration;
        this.state.readableDuration = this.formatTime(this.state.duration);
        this.state.canplay = true;
        break;

      case 'playing':
        this.state.playing = true;
        break;

      case 'pause':
        this.state.playing = false;
        break;

      case 'timeupdate':
        this.state.currentTime = this.audioObject.currentTime;
        this.state.readableCurrentTime = this.formatTime(this.state.currentTime);
        break;

      case 'error':
        this.resetState();
        this.state.error = true;
        break;
    }
    this.stateChange.next(this.state);
  }

  private resetState() {
    this.state = {
      playing: false,
      readableCurrentTime: '',
      readableDuration: '',
      duration: undefined,
      currentTime: undefined,
      canplay: false,
      error: false,
      mute: false,
    };
  }

  getState(): Observable<StreamState> {
    return this.stateChange.asObservable();
  }
audioplayer.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { StreamState } from './stream-state';
import { AudioService } from './audio.service';

@Component({
  selector: 'app-audio-player',
  templateUrl: './audio-player.component.html',
  styleUrls: ['./audio-player.component.scss'],
})
export class AudioPlayerComponent implements OnInit {
  @Input() audioUrl: string;
  @Input() ID: string;

  state: StreamState;

  constructor(private audioService: AudioService) {
    this.audioService.getState().subscribe(state => {
      this.state = state;
    });
  }

  ngOnInit(): void {
    console.log(this.audioUrl);
    this.playStream(this.audioUrl);
    this.pause();
  }
  playStream(url) {
    this.audioService.playStream(url).subscribe(events => {
      // console.log(events);
    });
  }

  pause() {
    this.audioService.pause();
  }
  play() {
    this.audioService.play();
  }
  stop() {
    this.audioService.stop();
  }
  mute() {
    this.audioService.mute();
  }
  unmute() {
    this.audioService.unmute();
  }
  onSliderChangeEnd(change) {
    this.audioService.seekTo(change.value);
  }
}
流状态
export interface StreamState {
  playing: boolean;
  readableCurrentTime: string;
  readableDuration: string;
  duration: number | undefined;
  currentTime: number | undefined;
  canplay: boolean;
  error: boolean;
  mute: boolean;
}
example.component.html
<div>
<app-audio-player audioUrl="audiofile.mp3"></app-audio-player>
<app-audio-player audioUrl="newaudio.mp3"></app-audio-player>
</div>

请采取什么措施以确保audioplayer组件的每个实例都是独立的。
谢谢。
更新。
这是here到stackblitz的链接

最佳答案

您能否在https://stackblitz.com/上更新此示例。我们将能够为您提供更好的帮助。
谢谢。

关于javascript - 如何使用angular创建可重用的音频播放器组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64354005/

相关文章:

javascript - 从页面上的链接获取ID

html - 如何正确设置这些丰富网页摘要的格式?

html - VBA:从 HTML 中获取数据

angular - 输入类型 ="number"不能以 Angular 形式工作

javascript - 从验证表单返回空字段名称

javascript - 使用 React js 调用两个以上的 Web 服务并将它们填充到 Material ui 下拉列表中

javascript - Angular 2 : trying to update ngModel with onModelChange inside the controller

angular - ionic 2 : How to use ViewChild instead of app. getComponent

Angular AWS Amplify 身份验证器额外字段

javascript - 如何在单击按钮时打开下拉菜单?