I am trying to create a reusable audio player component that I can use throughout my project. I followed this tutorial in creating the audio player itself.
现在的问题是,我决定让音频播放器自己成为一个可以在其他组件中呈现的组件,然后使用@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组件的每个实例都是独立的。 谢谢。