Кнопка playPause не сбрасывается при пропуске дорожки - PullRequest
1 голос
/ 05 мая 2020

Итак, это был мой первый раз, когда я использовал JavaScript, я обнаружил несколько ошибок с плеером. Помимо некоторых проблем CSS, в настоящее время не работают только две вещи. Во-первых, основной проблемой для этого поста является то, что кнопка воспроизведения / паузы не сбрасывается, когда я меняю песни. Чтобы воспроизвести следующую песню, мне нужно дважды щелкнуть, сделать паузу для воспроизведения и воспроизвести до паузы (чтобы затем начать песню), также не работает функция автоматического воспроизведения nextSong, что, как я предполагаю, связано?

Вторая небольшая проблема - название трека не меняется. Строка HTML для трека, который вы видите, это

            <div class="song-title">Track1</div>

Визуализация игрока

const background = document.querySelector('#background');
const thumbnail = document.querySelector('#thumbnail');
const song = document.querySelector('#song');

const songArtist = document.querySelector('.song-artist');
const songTitle = document.querySelector('.song-title');
const progressBar = document.querySelector('#progress-bar');
let pPause = document.querySelector('#play-pause');

songIndex = 0;
    songs = ['/music/track1.mp3',   '/music/track2.mp3',     '/music/track3.mp3',   '/music/track4.mp3',   '/music/track5.mp3',   '/music/track6.mp3',   '/music/track7.mp3'];
    thumbnails = ['/images/J&G Logo.png', '/images/J&G Logo.png', '/images/J&G Logo.png', '/images/J&G Logo.png', '/images/J&G Logo.png', '/images/J&G Logo.png', '/images/J&G Logo.png', ];
    songArtists = ['Jelly & The GOAT', 'Jelly & The GOAT', 'Jelly & The GOAT', 'Jelly & The GOAT', 'Jelly & The GOAT', 'Jelly & The GOAT', 'Jelly & The GOAT',];
    songTitles = ["Track1", "Track2", "Track3", "Track4", "Track5", "Track6", "Track7"];

let playing = true;
    function playPause()    {
        if (playing)    {
            const song = document.querySelector('#song'),
            thumbnail = document.querySelector('#thumbnail');

            pPause.src = "/images/pause-icon.png"
            thumbnail.style.transform = "scale(1.15)";

            song.play();
            playing = false;
        } else {
            pPause.src = "/images/play-icon.png"
            thumbnail.style.transform = "scale(1)";

            song.pause();
            playing = true;
        }
}

song.addEventListener('ended', function(){
    nextSong();
});

function nextSong() {
    songIndex++;
    if (songIndex === songs.length)  {
        songIndex = 0;
    };
    song.src = songs[songIndex];
    thumbnail.src = thumbnails[songIndex];
    background.src = thumbnails[songIndex];

    songArtist.innerHTML = songArtists[songIndex];
    songTitle.innerHTML = songTitles[songIndex];

    playing = true;
    playPause();
}

function previousSong() {
    songIndex--;
    if (songIndex < 0)  {
        songIndex = songs.length - 1;
    };
    song.src = songs[songIndex];
    thumbnail.src = thumbnails[songIndex];
    background.src = thumbnails[songIndex];

    songArtist.innerHTML = songArtists[songIndex];
    songTitle.innerHTML = songTitles[songIndex];

    playing = true;
    playPause();
}

function updateProgressValue()  {
    progressBar.max = song.duration;
    progressBar.value = song.currentTime;
    document.querySelector('.currentTime').innerHTML = (formatTime(Math.floor(song.currentTime)));
    if (document.querySelector('.durationTime').innerHTML === "NaN:NaN")    {
        document.querySelector('.durationTime').innerHTML = "0:00";
    }   else {
        document.querySelector('.durationTime').innerHTML = (formatTime(Math.floor(song.duration)));
    }
};

function formatTime(seconds)    {
    let min = Math.floor((seconds / 60));
    let sec = Math.floor(seconds - (min * 60));
    if (sec < 10){
        sec = `0${sec}`;
    };
    return `${min}:${sec}`;
};

setInterval (updateProgressValue, 500);

function changeProgressBar()    {
    song.currentTime    =   progressBar.value;
};

Пожалуйста, дайте мне знать, если вам понадобится HTML. Просто примечание, значок переключается нормально. Спасибо за любую помощь, любые объяснения будут очень благодарны :)!

1 Ответ

0 голосов
/ 05 мая 2020

Расхождения

Термин медиа-тег является общим c именем для <audio> и <video> тегов.

  • OP не имеет каких-либо прослушивателей событий или свойств события , поэтому можно с уверенностью предположить, что есть атрибуты события :

    <button onclick='lame()' type='button'>Click Me I'm Lame</button>
    

    Не используйте их, они отстой. Вместо этого используйте:

    <button type='button'>Click Me</button>
    
    const btn = document.queryselector('button');
    
    btn.addEventListener('click', clickHandler);
    
     /* OR */
    
    btn.onclick = clickHandler;
    
  • Там есть логическое значение isPlaying. Две проблемы:

    1. Первоначально не должно быть true. Здравый смысл подсказывает, что song не воспроизводится после загрузки страницы (если это так, подумайте об этом ради UX).

    2. Вместо использования флага используйте свойства мультимедиа, которые возвращают логическое значение, относящееся к состоянию тега мультимедиа (ie song): song.paused, song.ended и song.playing.

  • Первое if условное выражение nextSong() неверно:

    if (songIndex === songs.length)  {...
    

    songs.length равно 7. somgIndex диапазон 0-6. Значит, должно быть songs.length -1

  • Не включал тег <progress> и его обработчики, похоже, он скопирован правильно ... почти. Похоже, что это было добавлено позже:

    setInterval (updateProgressValue, 500);
    
    function changeProgressBar()    {
      song.currentTime = progressBar.value;
    };
    

    setInterval() - плохая замена события "timeupdate". Медиа-тег запускает "timeupedate" каждые 250 мс во время воспроизведения. Используйте свойства song.currentTime и song.duration для отслеживания прошедшего времени во время воспроизведения.

    changeProgressBar() выглядит бесполезным. Уже есть обработчик под названием updateProgressValue(). В единственной строке, содержащейся в changeProgressBar(), является обратная версия одной из строк в updateProgressValue():

    song.currentTime = progressBar.value;
    progressBar.value = song.currentTime;
    

Advice

Термин элементы управления формой является общим c термином для <input>, <output>, <textarea>, <button>, <select>, <fieldset> и <object> тегов.
Термин тег предка - это термин для тега, который разделяет ту же ветвь в дереве DOM на более высоком уровне, чем цели - термин для тегов-потомков тегов-предков, которые разработчик намеревается придать поведению, вызванному событием.

  • Если ваш макет имеет более одного элемента управления формой, заключите все в тег <form>. Это позволяет использовать HTMLFormElement интерфейс и HTMLFormControlsCollection API . Синтаксис краткий и упрощает доступ к элементам управления формы.

     <form id='main'>
       <fieldset name='set'>
         <input>
         <button type='button'>
           [type='button'] is needed when nested within a <form>...
         </button>
         <button>
           ...otherwise it is a [type='submit'] by default.
         </button>
       </fieldset>
       <fieldset name='set'>
         <input id='input1'>
         <input id='input2'>
       </fieldset>
     </form>
     <script>
       /*
       - Referencing the <form> by #ID
       - Or bracket notation document.forms['main'];
       - Or by index document.forms[0];
       */
       const main = document.forms.main;
    
       /*
       - Once the <form> is referenced -- use the `.elements` property to collect 
         all of its form controls into a **HTMLCollection** (Use an short and easy
         to remember identifier to reference it).
       */
    
       const ui = main.elements; 
       /*
       - <form> tags and form controls can be referenced by [name] attribute as 
         well. Keep in mind that if there is more than one tag with the same     
         [name] -- then they will be collected into a **HTMLCollection**.
       - Note: There is another fieldset[name=set] -- so in this situation you can
         access them both: `sets[0]` and `sets[1]`
       */
       const sets = ui.set;
    
       /*
       - To reference a form control without a #ID or [name], its index is always
         available. There's an archaic way as well: `const input = ui.item(1)`
       - There's no need to reference the second <button> because its a 
         [type='submit'] tag. They have default behavior built-in: *Sends data to
         server* and *Resets the <form>*
       */
       const input = ui[1];
       const button = ui[2];
    
       /*
       - Of the many ways to reference a form control -- #ID is the easiest.
       */
       const i1 = ui.input1;
       const i2 = ui.input2;
    </script>
    

  • Делегирование событий - это шаблон программирования который использует Event Bubbling , чтобы мы ...

    • ... могли прослушивать события, инициированные не только по неизвестному и неограниченному количеству тегов - мы также может прослушивать запускаемые события для тегов, которые динамически создаются после загрузки страницы.

    • Более того, для прослушивания событий необходим только один тег-предок, который есть у всех целевых тегов, ( window, document и body доступны всегда, но используются только в том случае, если нет другого тега, более близкого ко всем целям).

      document.forms[0].onclick = controlPlayer; 
      
    • обработчик событий функция должна передавать объект события , чтобы свойство .target можно было использовать для определения фактического тег, который взаимодействовал с пользователем (в данном случае, по нажатию).

      function controlPlayer(event) {
        const clicked = event.target;
      ...
      
    • Один раз t что установлено, мы сужаем возможности еще больше. Это позволяет нам исключить теги, которые нам не нужны, путем их явного исключения:

      if (event.target.matches('[type=button]')) {...
      
  • Изменение внешнего вида тегов для указания состояния может выполняется переключением .classes, стили которого уникальны для состояния, которое оно представляет. Обычной практикой является использование псевдоэлементов ::before и ::after


Demo

const thumb = document.querySelector('.thumb');
const song = document.querySelector('#song');

const form = document.forms.ui;
const ui = form.elements;
const artist = ui.artist;
const title = ui.title;
const play = ui.playBack;
const prev = ui.prev;
const next = ui.next;

const base = 'https://glpjt.s3.amazonaws.com/so/av';
const songs = ['/mp3/righteous.mp3', '/mp3/jerky.mp3', '/mp3/nosegoblin.mp3', '/mp3/balls.mp3', '/mp3/behave.mp3'];
const thumbs = ['/img/link.gif', '/img/sol.png', '/img/ren.gif', '/img/balls.gif', '/img/austin.gif'];
const artists = ['Samuel L. Jackson', 'Sol Rosenberg', 'Ren', 'Al Pachino', 'Mike Myers'];
const titles = ["Righteous", "Whatnot", "Magic Nose Goblins", "Balls", "Behave"];
let index = 0;
let quantity = songs.length;

const playMP3 = () => {
  play.classList.remove('play');
  play.classList.add('pause');
  song.play();
}

const pauseMP3 = () => {
  play.classList.remove('pause');
  play.classList.add('play');
  song.pause();
}

form.onclick = controlPlayer;

song.onended = function(event) {
  pauseMP3();
  index++;
  if (index > quantity - 1) {
    index = 0;
  }
  song.src = base + songs[index];
  thumb.src = base + thumbs[index];
  artist.value = artists[index];
  title.value = titles[index];
  playMP3();
}

function init() {
  song.src = base + songs[0];
  thumb.src = base + thumbs[0];
  artist.value = artists[0];
  title.value = titles[0];
  song.load();
}

function controlPlayer(event) {
  const clicked = event.target;

  if (clicked.matches('#playBack')) {
    if (song.paused || song.ended) {
      playMP3();
    } else {
      pauseMP3();
    }
  }

  if (clicked.matches('#prev')) {
    pauseMP3();
    index--;
    if (index < 0) {
      index = quantity - 1;
    }
    song.src = base + songs[index];
    thumb.src = base + thumbs[index];
    artist.value = artists[index];
    title.value = titles[index];
    playMP3();
  }

  if (clicked.matches('#next')) {
    pauseMP3();
    index++;
    if (index > quantity - 1) {
      index = 0;
    }
    song.src = base + songs[index];
    thumb.src = base + thumbs[index];
    artist.value = artists[index];
    title.value = titles[index];
    playMP3();
  }
}

init();
:root,
body {
  font: 700 small-caps 2.5vw/1.5 Verdana;
}

b {
  display: inline-block;
  width: 6ch;
  color: white;
}

output {
  color: gold;
  text-shadow: 0 0 5px red;
}

button {
  display: inline-block;
  width: 3vw;
  height: 11.25vw;
  line-height: 11.25vw;
  border: 0;
  font: inherit;
  font-size: 3rem;
  vertical-align: middle;
  color: lime;
  background: none;
  cursor: pointer;
}

button:hover {
  color: cyan;
}

#playBack.play::before {
  content: '\0025b6';
}

#playBack.pause {
  height: 5.625vw;
  line-height: 5.625vw;
}

#playBack.pause::before {
  content: '\00258e\a0\00258e';
  height: 5.625vw;
  line-height: 5.625vw;
  font-size: 2rem;
  vertical-align: top;
}

#prev::before {
  content: '\0023ee';
  height: 5vw;
  line-height: 5vw;
  font-size: 3.25rem;
  vertical-align: top;
}

#next,
#prev {
  height: 5vw;
  line-height: 5vw;
}

#next::before {
  content: '\0023ed';
  height: 5vw;
  line-height: 5vw;
  font-size: 3.25rem;
  vertical-align: top;
}

figure {
  position: relative;
  min-height: 150px;
  margin: 0px auto;
}

figcaption {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1;
  height: max-content;
  padding: 3px 5px;
  font-size: 1.2rem;
}

figcaption label {
  background: rgba(0, 0, 0, 0.4);
}

.thumb {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 5px auto;
  object-fit: contain;
}

.controls {
  display: flex;
  flex-flow: row nowrap;
  justify-content: space-evenly;
  align-items: center;
  height: 6vw;
}
<main>
  <form id='ui'>
    <audio id='song'></audio>
    <fieldset>
      <figure>
        <img class='thumb'>
        <figcaption>
          <label>
          <b>Artist:</b> <output id='artist'></output>
        </label>
          <label><br>
          <b>Title:</b> <output id='title'></output>
        </label>
        </figcaption>
      </figure>
      <fieldset class='controls'>
        <button id='prev' type='button'></button>
        <button id='playBack' class='play' type='button'></button>
        <button id='next' type='button'></button>
      </fieldset>
    </fieldset>
  </form>
</main>
...