• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Odtwarzacz muzyki na stronie

VPS Starter Arubacloud
+1 głos
382 wizyt
pytanie zadane 10 lutego 2022 w JavaScript przez CanoNee Nowicjusz (230 p.)

Dzień Dobry,

Tworzę odtwarzacz muzyki na stronę, który będzie domyślnie zawierał 14 playlist, jednak w trakcie pisania strony natknąłem się na jeden problem - wybrana playlista jest uruchamiana wyłącznie przez nieparzyste kliknięcia na stronie, każde parzyste kliknięcie powoduje problemy.

Łopatologicznie: 

  1. Klikam na playlistę A pojedynczym kliknięciem - działa bez zarzutu
  2. Następnie wybieram pojedynczym kliknięciem playlistę B - nie działa
  3. Wracam pojedynczym kliknięciem na playlistę A - działa bez zarzutu
  4. Następnie wybieram podwójnym kliknięciem playlistę B - działa bez zarzutu

Tak więc każde kliknięcie nr. 1, 3, 5, 7 etc. działa bez zarzutu, natomiast kliknięcia nr. 2, 4, 6, 8 nie działają, nie odtwarzają muzyki, nie ma również możliwości pauzowania i odpauzowania playlisty.

Kod:

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>RADIO</title>
    <link rel="stylesheet" href="style2.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto&display=swap">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css">
</head>
<body>
    <div id="menu">
        <div id="buttons">
            <div class="button-L" onclick="document.body.style.backgroundImage = 'url(img/1.jpg)';"><button value="W" onclick="gra(this)">Playlista 1</button></div>
            <div class="button-S" onclick="document.body.style.backgroundImage = 'url(img/2.jpg)';"><button value="W1" onclick="gra(this)">Playlista 2</button></div>
            <div class="button-S" onclick="document.body.style.backgroundImage = 'url(img/3.jpg)';"><button value="W2" onclick="gra(this)">Playlista 3</button></div>
            <div class="button-S" onclick="document.body.style.backgroundImage = 'url(img/4.jpg)';"><button value="W3" onclick="gra(this)">Playlista 4</button></div>
        </div>
    </div>

    <div class="music-container" id="music-container">
        <div class="music-info">
          <h4 id="title"></h4>
          <div class="progress-container" id="progress-container">
            <div class="progress" id="progress"></div>
          </div>
        </div>
  
        <audio src="music/sport.mp3" id="audio"></audio>
  
        <div class="img-container">
          <img src="img/cd-w3.jpg" alt="music-cover" id="cover">
        </div>

        <div class="navigation">
          <button id="prev" class="action-btn">
            <i class="fas fa-backward"></i>
          </button>

          <button id="play" class="action-btn action-btn-big">
            <i class="fas fa-play"></i>
          </button>

          <button id="next" class="action-btn">
            <i class="fas fa-forward"></i>
          </button>
        </div>
      </div>

    <script type="text/javascript">
        function gra(tytul) {
            var tytulGry = tytul.value;
            var songs = [];
            
            // Song titles
            if(tytulGry === "W") songs = ['The Trail', 'Kaer Morhen'];
            else if(tytulGry === "W1") songs = ['After The Storm', 'Spikeroog'];
            else if(tytulGry === "W2") songs = ['Trauma', 'Foxkids'];
            else if(tytulGry === "W3") songs = ['Myspace', 'Sport'];
        
            const musicContainer = document.getElementById('music-container');
            const playBtn = document.getElementById('play');
            const prevBtn = document.getElementById('prev');
            const nextBtn = document.getElementById('next');

            const audio = document.getElementById('audio');
            const progress = document.getElementById('progress');
            const progressContainer = document.getElementById('progress-container');
            const title = document.getElementById('title');
            const cover = document.getElementById('cover');
            const currTime = document.querySelector('#currTime');
            const durTime = document.querySelector('#durTime')
            
            // Keep track of song
            let songIndex = 1;

            // Initially load song details into DOM
            loadSong(songs[songIndex]);

            // Update song details
            function loadSong(song) {
            title.innerText = song;
            audio.src = `music/`+song+`.mp3`;
            cover.src = `img/`+tytulGry+`.jpg`;
            }

            // Play song
            function playSong() {
            musicContainer.classList.add('play');
            playBtn.querySelector('i.fas').classList.remove('fa-play');
            playBtn.querySelector('i.fas').classList.add('fa-pause');

            audio.play();
            }

            // Pause song
            function pauseSong() {
            musicContainer.classList.remove('play');
            playBtn.querySelector('i.fas').classList.add('fa-play');
            playBtn.querySelector('i.fas').classList.remove('fa-pause');

            audio.pause();
            }

            // Previous song
            function prevSong() {
            songIndex--;

            if (songIndex < 0) {
                songIndex = songs.length - 1;
            }

            loadSong(songs[songIndex]);

            playSong();
            }

            // Next song
            function nextSong() {
            songIndex++;

            if (songIndex > songs.length - 1) {
                songIndex = 0;
            }

            loadSong(songs[songIndex]);

            playSong();
            }

            // Update progress bar
            function updateProgress(e) {
            const { duration, currentTime } = e.srcElement;
            const progressPercent = (currentTime / duration) * 100;
            progress.style.width = `${progressPercent}%`;
            }

            // Set progress bar
            function setProgress(e) {
            const width = this.clientWidth;
            const clickX = e.offsetX;
            const duration = audio.duration;

            audio.currentTime = (clickX / width) * duration;
            }

            //get duration & currentTime for Time of song
            function DurTime (e) {
                const {duration,currentTime} = e.srcElement;
                var sec;
                var sec_d;

                // define minutes currentTime
                let min = (currentTime==null)? 0:
                Math.floor(currentTime/60);
                min = min <10 ? '0'+min:min;

                // define seconds currentTime
                function get_sec (x) {
                    if(Math.floor(x) >= 60){
                        
                        for (var i = 1; i<=60; i++){
                            if(Math.floor(x)>=(60*i) && Math.floor(x)<(60*(i+1))) {
                                sec = Math.floor(x) - (60*i);
                                sec = sec <10 ? '0'+sec:sec;
                            }
                        }
                    }else{
                        sec = Math.floor(x);
                        sec = sec <10 ? '0'+sec:sec;
                    }
                } 

                get_sec (currentTime,sec);

                // change currentTime DOM
                currTime.innerHTML = min +':'+ sec;

                // define minutes duration
                let min_d = (isNaN(duration) === true)? '0':
                    Math.floor(duration/60);
                min_d = min_d <10 ? '0'+min_d:min_d;


                function get_sec_d (x) {
                    if(Math.floor(x) >= 60){
                        
                        for (var i = 1; i<=60; i++){
                            if(Math.floor(x)>=(60*i) && Math.floor(x)<(60*(i+1))) {
                                sec_d = Math.floor(x) - (60*i);
                                sec_d = sec_d <10 ? '0'+sec_d:sec_d;
                            }
                        }
                    }else{
                        sec_d = (isNaN(duration) === true)? '0':
                        Math.floor(x);
                        sec_d = sec_d <10 ? '0'+sec_d:sec_d;
                    }
                } 

                // define seconds duration
                
                get_sec_d (duration);

                // change duration DOM
                durTime.innerHTML = min_d +':'+ sec_d;
                    
            };

            // Event listeners
            playBtn.addEventListener('click', () => {
            const isPlaying = musicContainer.classList.contains('play');

            if (isPlaying) {
                pauseSong();
            } else {
                playSong();
            }
            });

            // Change song
            prevBtn.addEventListener('click', prevSong);
            nextBtn.addEventListener('click', nextSong);

            // Time/song update
            audio.addEventListener('timeupdate', updateProgress);

            // Click on progress bar
            progressContainer.addEventListener('click', setProgress);

            // Song ends
            audio.addEventListener('ended', nextSong);

            // Time of song
            audio.addEventListener('timeupdate',DurTime);
        }
    </script>
</body>
</html>

Piosenki są plikami .mp3, których nazwy są przechowywane w zmiennej songs[]; Gdzie leży źródło problemu? Liczę na odpowiedź.

Pozdrawiam.

1
komentarz 17 lutego 2022 przez VBService Ekspert (251,210 p.)
edycja 17 lutego 2022 przez VBService

Zmienne globalne "ułatwiają życie", ale też mają swoje "ciemne" oblicze.  smiley

zobacz ten kod

// Globalne
let value1 = 'value1 - globalna';
console.info('1 '+value1);

function a() {
  let value1 = 'value1 - lokalna';
  console.log('2 '+value1);
}

function b() {
  console.log('3 '+value1);
}

function c() {
  // Taka zmiana może przyprawić 
  // o ból głowy w szukaniu błędu
  value1 = 'value1 - globalna zmieniona';
  console.log('4 '+value1);  
}

a();
b();
c();

także trzeba je używać "rozważnie"  wink, lub np. korzystać do przechowywania wartości zmiennych, które "muszą przechodzić" między funkcjami, to nie jest jedyne rozwiązanie, ale Ja np. korzystam z dataset

 

pewnie zauważyłeś w Moim kodzie zapis

audio.dataset.isPlayed = true;

co skutkuje w html-u (DOM strony)

<audio data-is-played="true" ...>
if (audio.dataset.isPlayed === 'true') songPlay();

ty też podszedłeś do tego "sprytnie"

const isPlaying = musicContainer.classList.contains('play'); 

przez sprawdzenie konkretnej klasy css przypisanej do elementu.

1
komentarz 17 lutego 2022 przez VBService Ekspert (251,210 p.)
edycja 17 lutego 2022 przez VBService

Ten zapis nie jest błędny, ale taki zapis `` wykorzystuje się template strings czy też template literals.

audio.src = 'music/'+song+'.mp3';

audio.src = "music/"+song+".mp3";

audio.src = `music/${song}.mp3`;

lub coś na kształt "template"

<pre></pre>

<script>
  const pre = document.querySelector('pre');
  let song = 'Vergen by Night';
  
  const text1 = 'Mój ulubiony utwór to "<<song>>". Jest super!';
  console.log(text1.replace('<<song>>', song));
  pre.textContent = text1.replace('<<song>>', song);
  
  pre.textContent += '\n';
  
  const text2 = 'Mój ulubiony utwór to %song%. Jest super!';
  console.info(text2.replace('%song%', song));
  pre.textContent += text2.replace('%song%', song);
  
  pre.textContent += '\n';
  
  const text3 = 'Mój ulubiony utwór to *song*. Jest super!';
  song = '<b>Vergen by Night</b>';
  console.warn(text3.replace('*song*', song));
  pre.innerHTML += text3.replace('*song*', song);
</script>

 

1
komentarz 17 lutego 2022 przez CanoNee Nowicjusz (230 p.)

Zmienne globalne "ułatwiają życie", ale też mają swoje "ciemne" oblicze.  smiley

Zdecydowanie laugh. Absolutna racja, już widzę różnicę w zmiennych. Widząc taki błąd, że działa co drugie kliknięcie myślałem, że problem jest bezpośrednio z którąś funkcją, skoro (de facto) klikając na daną playlistę wywołujemy cały kod na nowo i z początku działa bez zarzutu, a okazało się, że błąd już jest na samym początku i jednak zmienne potrafią namieszać...

także trzeba je używać "rozważnie"  wink,

Problemem już na etapie pisania okazało się to, iż pracę rozpocząłem z jedną playlistą, inne chciałem dodać, gdy skrypt byłby już na etapie finalizacji. Napisałem kilkaset linijek, wszystko po drodze testowałem, wszystko mi działało, natomiast problemem okazało się już dodanie kolejnych playlist, myślałem, że wystarczy wyłącznie zmiana zawartości tablicy songs[] w zależności od wartości przekazanej z HTML'a. Powinienem był najpierw zrobić dwie playlisty i od razu testować każdą możliwą funkcjonalność, być może wtedy problem wyszedłby dużo wcześniej.

ty też podszedłeś do tego "sprytnie" 

przez sprawdzenie konkretnej klasy css przypisanej do elementu.

To prawda, zmiana DOM strony z pewnością jest bardziej profesjonalna, zmiana CSS jest raczej drogą na skróty, natomiast do tej pory robiłem mniejsze projekty, głównie dla siebie, zmiana CSS była więc dla mnie bardziej łopatologiczna. Z pewnością trzeba będzie się bardziej zagłębić w DOM strony dla przyszłych projektów wink.

Ten zapis nie jest błędny, ale taki zapis `` wykorzystuje się template strings czy też template literals.

audio.src = 'music/'+song+'.mp3';
 
audio.src = "music/"+song+".mp3";
 
audio.src = `music/${song}.mp3`;

Z początku chciałem skorzystać z interpolacji w tym miejscu, jednak szukając błędu próbowałem doszukać się i tam, więc zrezygnowałem z zapisu ${song} na rzecz +song+, jednak backticki `` pozostały, w głównej mierze przez nieuwagę, ale przyznam się, że nie przywiązywałem wtedy do tego aż tak wielkiej wartości frown.

Dziękuję bardzo za wyjaśnienie pewnych aspektów, za linki (z pewnością się przydadzą), a przede wszystkim za pomoc w rozwiązywaniu problemu! smiley​​​​​​​

komentarz 17 lutego 2022 przez VBService Ekspert (251,210 p.)
edycja 17 lutego 2022 przez VBService

To prawda, zmiana DOM strony z pewnością jest bardziej profesjonalna, zmiana CSS jest raczej drogą na skróty,

To nie jest zmiana DOM w takim znaczeniu a bardziej możliwość dodawania własnych atrybutów do elementów  DOM.

 

posłużę się przykładem z kodu odtwarzacza muzyki tutaj omawianego.

Zaraz po załadowaniu strony element <audio> wygląda tak

<audio id="audio"></audio>

po wybraniu utworu (przycisk z playlist-y)

<audio id="audio" 
       data-playlist-index="1" 
       data-playlist-length="3" 
       data-song-index="0" 
       src="https://freesound.org/data/previews/264/264295_4019029-lq.mp3">
</audio>

po przyciśnięciu przycisku play

<audio id="audio" 
       data-playlist-index="1" 
       data-playlist-length="3" 
       data-song-index="0" 
       data-is-played="true"
       src="https://freesound.org/data/previews/264/264295_4019029-lq.mp3">
</audio>

sam widzisz, że dataset jest bardzo "użyteczne" i w sumie proste w "obsłudze" (można atrybutów data-... dodać wiele), inny sposób to użycie localStorage.

 

Moim zdaniem zmienne globalne sprawdzają się w takich przypadkach:

- tablica (w Naszym przykładzie songs[])

const songs = [
  { 
    'bg_img':'https://picsum.photos/1500/1500/?random=1',
    'title':'India', 
    'list': [
      { 
        'link':'https://freesound.org/data/previews/350/350547_347704-lq.mp3',
        'title':'Kaiho - india sitar',
        'cover_front':'https://picsum.photos/50/50/?random=10',
        'cover_back':'https://picsum.photos/50/50/?random=101'
      },
      {
        'link':'https://freesound.org/data/previews/37/37715_347704-lq.mp3',
        'title':'Kaiho - india sitar tarb 12strings',
        'cover_front':'https://picsum.photos/50/50/?random=11',
        'cover_back':'https://picsum.photos/50/50/?random=111'
      }
  ]}, ... itd.

    ...
];

- zmienne przechowujące referencję do elementu DOM

let playlist_buttons_container, music_container, audio, 
     button_play, button_pause, button_prev, button_next;

window.addEventListener('load', load);

function load() {
  playlist_buttons_container = document.querySelector('#menu #buttons');
  playlist_buttons_container.addEventListener('click', startPlaylist);

  audio = document.querySelector('#audio');

  music_container = document.querySelector('#music-container');
  button_play  = music_container.querySelector('#play');
  button_pause = music_container.querySelector('#pause');
  button_prev  = music_container.querySelector('#prev');
  button_next  = music_container.querySelector('#next');  

  button_play.addEventListener('click', songPlay);
  button_pause.addEventListener('click', songPause);

  ... itd
}

 

komentarz 17 lutego 2022 przez VBService Ekspert (251,210 p.)
edycja 17 lutego 2022 przez VBService

a wracając do

zmiana CSS jest raczej drogą na skróty

sposób dobry, bo można

const isPlaying = musicContainer.classList.contains('play');
if (isPlaying) playSong();

ale "niebezpieczny", bo załóżmy taki scenariusz, dopiszesz 300-400 linii kodu w ciągu kolejnych 1-2 tygodni i zapomnisz o powyższym wpisie, a stwierdzisz, że chcesz dodać "ozdoby" w css-ie do musicContainer z poziomu kodu js-a i napiszesz funkcję

function demo() {
    if (warunek) {
        musicContainer.className = '';
        // jakiś kod
    }
}

i przy testach będziesz się "dziwił" dlaczego songPlay() nie działa prawidłowo.  wink przypisanie 'play' do <audio> za pomocą audio.dataset.play jest bardziej odporne na tego typu "wpadki". Jedno trzeba mieć na uwadze wartości w data-... są "przechowywane" jako typu string.

// boolean
audio.dataset.play = true; // przypisanie wartości - zapis
audio.dataset.play = false;
const is_play = (audio.dataset.play === 'true'); // odczyt

// integer
let zmienna = (10 * 3) - 2;
audio.dataset.playlistIndex = 3; // zapis
audio.dataset.playlistIndex = zmienna;
const playlist_index = parseInt(audio.dataset.playlistIndex) || 0; // odczyt

... itd

 

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

+1 głos
1 odpowiedź 297 wizyt
pytanie zadane 18 sierpnia 2021 w Nasze projekty przez qax Dyskutant (8,060 p.)
+2 głosów
3 odpowiedzi 974 wizyt
pytanie zadane 31 grudnia 2015 w Nasze projekty przez Tricker Bywalec (2,630 p.)
0 głosów
2 odpowiedzi 293 wizyt
pytanie zadane 12 maja 2016 w HTML i CSS przez Nicolaus Dyskutant (9,740 p.)

92,452 zapytań

141,262 odpowiedzi

319,079 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...