У меня есть следующий класс PlaylistItem
, который, кажется, не работает. Проблемными функциями c являются step
и seekBackOrForward
. seekBackOrForward
вызывается, когда пользователь прокручивает элемент HTML, представляющий игрока musi c. Когда пользователь выполняет прокрутку в секунду, он работает хорошо. Когда он делает больше прокруток в секунду, игрок показывает 0:00
как текущее время в течение небольшого времени или пока я не прокручиваю снова, на этот раз медленно.
export class PlaylistItem {
title: ko.Observable<string>;
file: ko.Observable<string>;
formattedDuration: ko.Computed<string>;
/**
* In seconds.
*/
duration: ko.Observable<number>;
isPlaying: ko.Observable<boolean>;
lastPosition: ko.Observable<number>;
howl: Howl;
saveSeek: number;
musicID: number | null;
state: ko.Observable<MusicPlayerState>;
audioFolderPath: string;
wave: any;
timerString: ko.Observable<string>;
timerPercent: ko.Observable<string>;
animation: number | null;
constructor(t: string, f: string, d: number, wave: any) {
this.title = ko.observable(t);
this.file = ko.observable(f);
this.duration = ko.observable(d);
this.isPlaying = ko.observable(false);
this.lastPosition = ko.observable(0);
this.state = ko.observable(MusicPlayerState.Startup);
this.wave = wave;
this.formattedDuration = ko.computed(() => {
return Utils.formatTime(Math.round(this.duration()));
});
this.howl = null;
this.timerString = ko.observable("0:00");
this.timerPercent = ko.observable("0%");
this.audioFolderPath = "/wp-content/themes/custom-theme/assets/audio";
this.animation = null;
this.saveSeek = 0;
this.musicID = null;
}
unpause() : void
{
if (this.howl === null)
{
return;
}
this.howl.play(this.musicID);
this.howl.seek(this.saveSeek, this.musicID);
this.isPlaying(true);
this.state(MusicPlayerState.Playing);
}
pause() : void
{
if (this.howl === null)
{
return;
}
this.howl.pause();
this.saveSeek = this.howl.seek(this.musicID);
this.state(MusicPlayerState.Paused);
}
play() : void
{
let bar: HTMLElement = document.getElementById('bar');
let pauseBtn: HTMLElement = document.getElementById('pauseBtn');
let loading: HTMLElement = document.getElementById('loading');
let self = this;
this.howl = new Howl({
src: [`${this.audioFolderPath}/${this.file()}.webm`,
`${this.audioFolderPath}/${this.file()}.mp3`],
html5: true, // Force to HTML5 so that the audio can stream in (best for large files).
onplay: () : void =>
{
// Start updating the progress of the track.
if (self.animation != null)
{
cancelAnimationFrame(self.animation);
self.animation = null;
}
self.animation = requestAnimationFrame(self.step.bind(self));
// Start the wave animation if we have already loaded
self.wave.container.style.display = 'block';
bar.style.display = 'none';
pauseBtn.style.display = 'block';
},
onload: () : void =>
{
// Start the wave animation.
self.wave.container.style.display = 'block';
bar.style.display = 'none';
loading.style.display = 'none';
},
onend: () : void =>
{
// Stop the wave animation.
self.wave.container.style.display = 'none';
bar.style.display = 'block';
self.state(MusicPlayerState.Playing);
self.howl.play(self.musicID);
},
onpause: () : void =>
{
// Stop the wave animation.
self.wave.container.style.display = 'none';
bar.style.display = 'block';
},
onstop: () : void =>
{
// Stop the wave animation.
self.wave.container.style.display = 'none';
bar.style.display = 'block';
},
onseek: () : void =>
{
if (self.animation != null)
{
cancelAnimationFrame(self.animation);
self.animation = null;
}
// Start upating the progress of the track.
self.animation = requestAnimationFrame(self.step.bind(self));
}
});
// Begin playing the sound.
self.musicID = self.howl.play();
self.state(MusicPlayerState.Playing);
}
/**
* The step called within requestAnimationFrame to update the playback position.
*/
step(): void
{
// Get the Howl we want to manipulate.
let sound: Howl | null = this.howl;
// Determine our current seek position.
let seek: number = <number>(sound.seek()) || 0; // TODO: what if it is a Howl?
if (sound === null)
{
this.timerString(Utils.formatTime(0));
this.timerPercent("0%");
}
else
{
this.timerString(Utils.formatTime(Math.round(seek)));
this.timerPercent((((seek / sound.duration()) * 100) || 0) + '%');
}
// If the sound is still playing, continue stepping.
if (sound.playing()) {
this.animation = requestAnimationFrame(this.step.bind(this));
}
}
/*
* Scrolls back or forward in the current song, depending on the mouse wheel direction.
* vm - the PageViewModel object.
* e - the wheel event object.
*/
seekBackOrForward(vm /* : PageViewModel */, e) : void
{
// the currently playing song
let item : PlaylistItem = vm.player.currentPlaylistItem(); // wudl have been `this`
// the song player object
let h: Howl = item.howl;
// if the song was not playing ever since the page loaded
if (h === null || h === undefined)
{
// do nothing.
return;
}
// get the position in the current song
let s: number = /* item.saveSeek */ h.seek();
// if (typeof s !== "number" || s < 2)
// {
// console.log(s);
// return;
// }
// if scrolling towards the screen
if (Math.sign(e.originalEvent.deltaY) === 1)
{
++s;
}
// else if not scrolling towards the screen but in the oposite direction
else
{
--s;
}
// seek to the next or previous second as chosen above
h.seek(s, item.musicID);
// save the new position for the case in which the melody is put on pause and the user unpauses later to this position
item.saveSeek = s;
}
}
(я знаю, что seekBackOrForward
должен получить доступ к this
не через метод, а через ключевое слово this
, но для меня это мелочь, которую я могу изменить в будущем.)
Код основан на первом официальном Howler. js демо здесь ( Musi c Player ). При необходимости я могу опубликовать любой другой соответствующий фрагмент кода.
Я попытался воспроизвести проблему в этом JSFiddle , но там нет проблем.
Я использую TypeScript, Howler . js, нокаут. js и Firefox.
Спасибо.