HTML + JavaScript 网站页面上多个视频的自定义播放器

HTML + JavaScript custom player for multiple videos on a website page

编辑:我找到了解决方案,在下面发布并标记为最佳答案。

我正在使用 HTML、CSS 和 JS 为自己编写一个作品集网站,我需要在很多页面上添加多个视频。

我按照一些教程学习了如何自定义视频播放器,但它只适用于页面上的一个特定视频。如果我要添加更多视频,我需要为每个视频创建一个 .js 自定义播放器文件,并在网站上手动 select 它们。

我如何才能将这个单一的 .js 自定义视频播放器应用到我所有的纯视频播放器 javascript?

我在这里找到了关于该主题的类似主题,但它们都使用 jQuery,我正在努力使其与 javascript 一起使用。

我的 HTML 视频播放器:

<section class="videoplayer">
<div class="c-video">
    <div id="video_player">
        <video src="./media/portfolio/videos/Show-Reel-2021.mp4" id="main-video"></video>
        <div class="progressAreaTime">00:00</div>
        <div class="controls">
            <div class="progress-area">
                <div class="progress-bar">
                    <span></span>
                </div>
                <div class="buffered-progress-bar"></div>
            </div>
            <div class="controls-list">
                <div class="controls-left">
                    <span class="icon">
                        <i class="material-icons fast-rewind" title="Retroceder 10 segundos">first_page</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons play_pause" title="Reproduzir">play_arrow</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons fast-forward" title="Avançar 10 segundos">last_page</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons volume" title="Sem áudio">volume_up</i>
                        <input type="range" min="0" max="100" class="volume_range">
                    </span>
                    <div class="timer">
                        <span class="current">00:00</span> / <span class="duration">0:00</span>
                    </div>
                </div>
                <div class="controls-right">
                    <span class="icon">
                        <i class="material-icons auto-play" title="A repetição automática está desativada"></i>
                    </span>
                    <span class="icon">
                        <i class="material-icons settingsBtn" title="Detalhes">settings</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons picture_in_picture" title="Miniplayer">picture_in_picture_alt</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons fullscreen" title="Tela inteira">fullscreen</i>
                    </span>
                </div>
            </div>
        </div>
        <div id="settings">
            <div class="playback">
                <span>Velocidade da Reprodução</span>
                <ul>
                    <li data-speed="0.25">0.25</li>
                    <li data-speed="0.5">0.5</li>
                    <li data-speed="0.75">0.75</li>
                    <li data-speed="1" class="active">Normal</li>
                    <li data-speed="1.25">1.25</li>
                    <li data-speed="1.5">1.5</li>
                    <li data-speed="1.75">1.75</li>
                    <li data-speed="2">2</li>
                </ul>
            </div>
        </div>
    </div>
</div>

我的 JavaScript 视频播放器:

// Select elements
const vidsrc = document.querySelector('#main-video').src;
const video_player = document.querySelector('#video_player'),
mainVideo = video_player.querySelector('#main-video'),
progressAreaTime = video_player.querySelector('.progressAreaTime'),
controls = video_player.querySelector('.controls'),
progressArea = video_player.querySelector('.progress-area'),
progress_Bar = video_player.querySelector('.progress-bar'),
buffered_Bar = video_player.querySelector('.buffered-progress-bar'),
fast_rewind = video_player.querySelector('.fast-rewind'),
play_pause = video_player.querySelector('.play_pause'),
fast_forward = video_player.querySelector('.fast-forward'),
volume = video_player.querySelector('.volume'),
volume_range = video_player.querySelector('.volume_range'),
current = video_player.querySelector('.current'),
totalDuration = video_player.querySelector('.duration'),
auto_play = video_player.querySelector('.auto-play'),
settingsBtn = video_player.querySelector('.settingsBtn'),
picture_in_picture = video_player.querySelector('.picture_in_picture'),
fullscreen = video_player.querySelector('.fullscreen'),
settings = video_player.querySelector('#settings'),
playback = video_player.querySelectorAll('.playback li');

mainVideo.addEventListener('loadeddata',()=>{
    setInterval(() => {
        let bufferedTime = mainVideo.buffered.end(0);
        let duration = mainVideo.duration;
        let width = (bufferedTime / duration) * 100;
        buffered_Bar.style.width = `${width}%`
    }, 500);
})

// Play
function playVideo() {
    play_pause.innerHTML = "pause";
    play_pause.title = "Pausar";
    video_player.classList.add('paused')
    mainVideo.play();
}

// Pause
function pauseVideo() {
    play_pause.innerHTML = "play_arrow";
    play_pause.title = "Reproduzir";
    video_player.classList.remove('paused')
    mainVideo.pause();
}

play_pause.addEventListener('click',()=>{
    const isVideoPaused = video_player.classList.contains('paused');
    isVideoPaused ? pauseVideo() : playVideo();
})

mainVideo.addEventListener('play',()=>{
    playVideo();
})

mainVideo.addEventListener('pause',()=>{
    pauseVideo();
})

// Rewind
fast_rewind.addEventListener('click',()=>{
    mainVideo.currentTime -= 10;
})

// Forward
fast_forward.addEventListener('click',()=>{
    mainVideo.currentTime += 10;
})

// Total duration
mainVideo.addEventListener("loadeddata",(e)=>{
    let videoDuration = e.target.duration;
    let totalMin = Math.floor(videoDuration / 60);
    let totalSec = Math.floor(videoDuration % 60);

    // Complete with zero at beggining
    totalSec < 10 ? totalSec = "0"+totalSec:totalSec;
    totalMin < 10 ? totalMin = "0"+totalMin:totalMin;
    totalDuration.innerHTML = `${totalMin}:${totalSec}`;
})

// Current duration
mainVideo.addEventListener('timeupdate',(e)=>{
    let currentVideoTime = e.target.currentTime;
    let currentMin = Math.floor(currentVideoTime / 60);
    let currentSec = Math.floor(currentVideoTime % 60);
   // Complete with zero at beggining
    currentSec < 10 ? currentSec = "0"+currentSec:currentSec; 
    currentMin < 10 ? currentMin = "0"+currentMin:currentMin; 
    current.innerHTML = `${currentMin}:${currentSec}`;

    let videoDuration = e.target.duration
    // Seek bar
    let progressWidth = (currentVideoTime / videoDuration) * 100;
    progress_Bar.style.width = `${progressWidth}%`;
})

// Update current duration
progressArea.addEventListener('click',(e)=>{
    let videoDuration = mainVideo.duration;
    let progressWidthval = progressArea.clientWidth;
    let ClickOffsetX = e.offsetX;
    mainVideo.currentTime = (ClickOffsetX / progressWidthval) * videoDuration;
})

// Volume
function changeVolume() {
    mainVideo.volume = volume_range.value / 100;
    if (volume_range.value == 0) {
        volume.innerHTML = "volume_off";
    }else if(volume_range.value < 40){
        volume.innerHTML = "volume_down";
    }else{
        volume.innerHTML = "volume_up";
    }
}

function muteVolume() {
    if (volume_range.value == 0) {
        volume_range.value = 80;
        mainVideo.volume = 0.8;
        volume.innerHTML = "volume_up";
        volume.title = "Sem áudio";
    }else{
        volume_range.value = 0;
        mainVideo.volume = 0;
        volume.innerHTML = "volume_off";
        volume.title = "Reativar o som";
    }
}

volume_range.addEventListener('change',()=>{
    changeVolume();
})

volume.addEventListener('click',()=>{
    muteVolume();
})

// Update on mouse move
progressArea.addEventListener('mousemove',(e)=>{
    let progressWidthval = progressArea.clientWidth;
    let x = e.offsetX;
    progressAreaTime.style.setProperty('--x',`${x}px`);
    progressAreaTime.style.display = "block";
    let videoDuration = mainVideo.duration;
    let progressTime = Math.floor((x/progressWidthval)*videoDuration);
    let currentMin = Math.floor(progressTime / 60);
    let currentSec = Math.floor(progressTime % 60);
   // Complete with zero at beggining
    currentSec < 10 ? currentSec = "0"+currentSec:currentSec; 
    currentMin < 10 ? currentMin = "0"+currentMin:currentMin; 
    progressAreaTime.innerHTML = `${currentMin}:${currentSec}`;
})

progressArea.addEventListener('mouseleave',()=>{
    progressAreaTime.style.display = "none";
})

// Loop
auto_play.addEventListener('click',()=>{
    auto_play.classList.toggle('active')
    if(auto_play.classList.contains('active')){
        auto_play.title = "A repetição automática está ativada";
    }else{
        auto_play.title = "A repetição automática está desativada";
    }
});

mainVideo.addEventListener("ended",()=>{
    if (auto_play.classList.contains('active')) {
        playVideo();
    }else{
        play_pause.innerHTML = "replay";
        play_pause.title = "Reproduzir novamente";
    }
});

// Picture in picture
picture_in_picture.addEventListener('click',()=>{
    mainVideo.requestPictureInPicture();
})

// Full screen
fullscreen.addEventListener('click',()=>{
    if (!video_player.classList.contains('openFullScreen')) {
        video_player.classList.add('openFullScreen');
        fullscreen.innerHTML = "fullscreen_exit";
        fullscreen.title = "Sair da tela inteira";
        video_player.requestFullscreen();
    }else{
        video_player.classList.remove('openFullScreen');
        fullscreen.innerHTML = "fullscreen";
        fullscreen.title = "Tela inteira";
        document.exitFullscreen();
    }
});

// Settings
settingsBtn.addEventListener('click',()=>{
    settings.classList.toggle('active');
    settingsBtn.classList.toggle('active');
})

// Speed
playback.forEach((event)=>{
    event.addEventListener('click',()=>{
        removeActiveClasses();
        event.classList.add('active');
        let speed = event.getAttribute('data-speed');
        mainVideo.playbackRate = speed;
    })
})

function removeActiveClasses() {
    playback.forEach(event => {
        event.classList.remove('active')
    });
}

// Get URL
let xhr = new XMLHttpRequest();
xhr.open("GET",vidsrc);
xhr.responseType = "arraybuffer";
xhr.onload = (e)=>{
    let blob = new Blob([xhr.response]);
    let url = URL.createObjectURL(blob);
    mainVideo.src = url;
}
xhr.send();

// Store duration
window.addEventListener('unload',()=>{
    let setDuration = localStorage.setItem('duration',`${mainVideo.currentTime}`);
    let setSrc = localStorage.setItem('src',`${mainVideo.getAttribute('src')}`);
})

window.addEventListener('load',()=>{
    let getDuration = localStorage.getItem('duration');
    let getSrc = localStorage.getItem('src');
    if (getSrc) {
        mainVideo.src = getSrc;
        mainVideo.currentTime = getDuration;
    }
})

mainVideo.addEventListener('contextmenu',(e)=>{
    e.preventDefault();
})

// Hide and show controls (mouse)
video_player.addEventListener('mouseover',()=>{
    controls.classList.add('active');
})

video_player.addEventListener('mouseleave',()=>{
    if (video_player.classList.contains('paused')) {
        if (settingsBtn.classList.contains('active')) {
            controls.classList.add('active');
        }else{
            controls.classList.remove('active')
        }
    }else{
        controls.classList.add('active')
    }
})

if (video_player.classList.contains('paused')) {
    if (settingsBtn.classList.contains('active')) {
        controls.classList.add('active');
    }else{
        controls.classList.remove('active')
    }
}else{
    controls.classList.add('active')
}

// Hide and show controls (mobile)
video_player.addEventListener('touchstart',()=>{
    controls.classList.add('active');
    setTimeout(() => {
        controls.classList.remove('active')
    }, 8000);
})

video_player.addEventListener('touchmove',()=>{
    if (video_player.classList.contains('paused')) {
        controls.classList.remove('active')
    }else{
        controls.classList.add('active')
    }
})

辅助函数,您可以使用它编写一次 HTML 框架,然后为多个实例动态创建它

function renderVideoPlayers() {

  const sources = [
    '../my-video-src-1',
    '../my-video-src-2',
    '../my-video-src-3',
    '../my-video-src-4',
  ]

  const videoPlayers = sources.map((video) => `
  <section class="videoplayer">
  <div class="c-video">
    <div id="video_player">
        <video src="${video}" class="video"></video>
        <div class="progressAreaTime">00:00</div>
        <div class="controls">
            <div class="progress-area">
                <div class="progress-bar">
                    <span></span>
                </div>
                <div class="buffered-progress-bar"></div>
            </div>
            <div class="controls-list">
                <div class="controls-left">
                    <span class="icon">
                        <i class="material-icons fast-rewind" title="Retroceder 10 segundos">first_page</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons play_pause" title="Reproduzir">play_arrow</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons fast-forward" title="Avançar 10 segundos">last_page</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons volume" title="Sem áudio">volume_up</i>
                        <input type="range" min="0" max="100" class="volume_range">
                    </span>
                    <div class="timer">
                        <span class="current">00:00</span> / <span class="duration">0:00</span>
                    </div>
                </div>
                <div class="controls-right">
                    <span class="icon">
                        <i class="material-icons auto-play" title="A repetição automática está desativada"></i>
                    </span>
                    <span class="icon">
                        <i class="material-icons settingsBtn" title="Detalhes">settings</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons picture_in_picture" title="Miniplayer">picture_in_picture_alt</i>
                    </span>
                    <span class="icon">
                        <i class="material-icons fullscreen" title="Tela inteira">fullscreen</i>
                    </span>
                </div>
            </div>
        </div>
        <div id="settings">
            <div class="playback">
                <span>Velocidade da Reprodução</span>
                <ul>
                    <li data-speed="0.25">0.25</li>
                    <li data-speed="0.5">0.5</li>
                    <li data-speed="0.75">0.75</li>
                    <li data-speed="1" class="active">Normal</li>
                    <li data-speed="1.25">1.25</li>
                    <li data-speed="1.5">1.5</li>
                    <li data-speed="1.75">1.75</li>
                    <li data-speed="2">2</li>
                </ul>
            </div>
        </div>
    </div>
</div>
`)

  // get an element already on the page add the rendered
  // html for each video into that section.
  const container = document.getElementById('container')
  container.innerHTML = videoPlayers.join("")
}

// call this function when you want it to be displayed
renderVideoPlayers()
<html>
<head>
</head>
<body>
<div id="container">
</div>
</body>
</html>

您还需要对 Javascript 执行相同的操作,并确保同一 id 没有多个实例。这应该不会太难,因为您可以将类似的样式切换为 class 并使 id 类似于 video-1video-2video-3

几个笔记

  • sources: 这是您希望在页面上显示的所有视频网址的数组。
  • sources.map:将遍历数组并用视频播放器HTML和视频源
  • 插入一个字符串
  • videoPlayers:现在是 HTML 标记数组
  • container:将是您希望附加所有 HTML 标记的页面上的父元素

我找到了解决方案

对于将来寻求相同解决方案的任何人,我能够通过首先选择每个视频播放器来使其工作div:

const videoContainers = document.querySelectorAll(".video-container")

然后使用 .forEach() 函数,这样代码会为每个 individual videoplayer 生成一个按钮:

videoContainers.forEach((container) => {
  let playPauseBtn = container.querySelector(".play-pause-btn");
  let theaterBtn = container.querySelector(".theater-btn");
  let fullScreenBtn = container.querySelector(".full-screen-btn");
...

// The entire code for one videoplayer
}

您可以同时参考div同一视频播放器的“容器”或每个视频播放器的“文档”。它就像魔法一样有效!