ساخت ویدیو پلیر حرفه ای با جاوا اسکریپت ( لیست پخش ، پیش نمایش فریم )

ساخت ویدیو پلیر حرفه ای با جاوا اسکریپت ( لیست پخش ، پیش نمایش فریم )

ویدیو پلیر با جاوا اسکریپت – در این آموزش با javascript خالص بدون هیچ فریمورک و کتابخانه ای ویدیو پلیر حرفه ای و شخصی سازی شده ی خودمان را می سازیم .

ویدیو پلیر اختصاصی با امکان playlist و پیش نمایش هر فریم ثانیه را در زمان بردن ماوس بر روی seekbar را دارد .

برای درک بهتر ویدیوی معرفی را تماشا نمایید :

ویدیوی معرفی پلیر اختصاصی با js


فایل index.html

<!DOCTYPE html>
<html lang="fa" id="xhtml">
<head>
    <meta charset="UTF-8">
    <title>Rapidcode.iR - سورس کد</title>
    <link rel="stylesheet" href="static/css/main.css">
</head>
<body>
    <div class="container">
        <a id="introduce" href="https://rapidcode.ir" target="_blank">رپید کد • کتابخانه مجازی برنامه نویسان</a>
        <div class="video-wrapper">
            <div id="preview-thumbnail">
                <img src="static/video/thumbnails/Lamborghini Urus (1).png">
            </div>
            <ul id="video-playlist">
                <li data-src="static/video/Lamborghini Urus.mp4">لامبورگینی اوروس</li>
                <li data-src="static/video/FORD Mustang GT.mp4">فورد موستانگ نیو</li>
            </ul>
            <div class="video-player-wrapper">
            <video id="my-custom-video-play" src="static/video/Lamborghini Urus.mp4"></video>
            <div class="video-controls">
                <span class="icon-player play-pause"></span>
                <div data-preview="true" id="timer" class="seekbar timer">
                    <div data-preview="true" id="loaded"></div>
                    <div data-preview="true" id="progress"><div data-preview="true" id="handler"></div></div>
                    <span id="remain">-00:00</span>
                </div>
                <span class="icon-player sound-mute"></span>
                <div class="seekbar sound">
                    <div id="loaded"></div>
                    <div id="progress"><div id="handler"></div></div>
                </div>
                <span class="icon-player full-screen"></span>
            </div>
        </div>
        </div>
    </div>
    <script src="static/js/app.js"></script>
</body>
</html>


اسکریپت app.js جهت کنترل بر رویداد ها و کنترل پلیر

// DOM
const videoDOM = document.getElementById('my-custom-video-play');
const seekbarDOM = document.getElementsByClassName('seekbar');
const seekbarTimersDOM = document.querySelector('.seekbar.timer');
const seekbarVolumeDOM = document.querySelector('.seekbar.sound');
const playpauseDOM = document.getElementsByClassName('play-pause')[0];
const soundmuteDOM = document.getElementsByClassName('sound-mute')[0];
const remainDOM = document.getElementById('remain');
const fullScreenDOM = document.getElementsByClassName('full-screen')[0];
const previewThumbnailDOM = document.getElementById('preview-thumbnail');
const videoPlaylistDOM = document.querySelectorAll('#video-playlist li');

// Property
const isMuted = videoDOM.hasAttribute("muted");
const isAutoplay = videoDOM.hasAttribute("autoplay");
var videoInterval;

// init
setInitialVideoOptions();

// set handlers
Array.from(seekbarDOM).forEach((element) => {
    element.addEventListener("click", seekbarDOMHandlerClicked);
    element.addEventListener("mousemove", seekbarDOMHandlerClicked);
    element.addEventListener("mousemove", seekbarDOMHandlerShowThumbnail);
});

Array.from(videoPlaylistDOM).forEach((element) => {
    element.addEventListener("click", videoPlaylistDOMHandlerClicked);
});



videoInterval = setInterval(VideoOnUpdateTime, 100)

playpauseDOM.addEventListener('click', playpauseDOMHandlerClicked);
fullScreenDOM.addEventListener('click', fullScreenDOMHandlerClicked);
window.addEventListener("dblclick", windowHandlerExitFullScreen);
window.addEventListener("keyup", windowHandlerShortcut);
window.addEventListener("mousemove", seekbarDOMHandlerShowThumbnail);


// handlers
function seekbarDOMHandlerClicked(event) {

    if (event.type != 'click' && event.buttons == 0) return false;

    const thisElement = this;
    const isSoundSeekbar = thisElement.classList.contains("sound");
    const isTimerSeekbar = thisElement.classList.contains("timer");

    if (isMuted && isSoundSeekbar) return false;

    const progressElement = thisElement.querySelector("#progress");
    const finallWidth = moveRangeSlider(thisElement , event)

    if (finallWidth < 0 || 100 < finallWidth) return;
    progressElement.style.width = finallWidth + "%";
    if (isTimerSeekbar) {
        videoDOM.currentTime = getNumberByPercent(finallWidth, videoDOM.duration);
        videoDOM.play()
    } else if (isSoundSeekbar) {
        if (thisElement.querySelector('#progress').clientWidth == 0) soundmuteDOM.classList.add("mute")
        else soundmuteDOM.classList.remove("mute")
        videoDOM.volume = finallWidth / 100;
    }
}

function videoPlaylistDOMHandlerClicked(){
    const thisElement = this;
    clearInterval(videoInterval);
    videoDOM.pause();
    const videoSrc = thisElement.dataset.src;
    videoDOM.src = videoSrc;
    videoDOM.load();
    
    videoDOM.onloadeddata = function(){
        setInitialVideoOptions();
        videoInterval = setInterval(VideoOnUpdateTime, 100)
        videoDOM.play()
    }
    makePlaylistElementActive(videoPlaylistDOM);
}

function seekbarDOMHandlerShowThumbnail(event){
    if(typeof event.target.hasAttribute == "undefined") return false;

    var videoFrame = getNumberByPercent(Math.round(moveRangeSlider(seekbarTimersDOM , event)), videoDOM.duration)

    if(!event.target.hasAttribute('data-preview') || (videoFrame < 0 || 100 < videoFrame)){
        previewThumbnailDOM.style.display = "none";
        return false;
    }

    videoFrame = Math.round(Math.abs(videoFrame)) == 0 ? 1 : Math.round(Math.abs(videoFrame));
    var videoName = videoDOM.getAttribute('src');
    videoName = videoName.substring(videoName.lastIndexOf('/'));
    videoName = videoName.replace(/\/|\.mp4/gi , "");
    const frameFileTemplate = `static/video/thumbnails/${videoName} (${videoFrame}).png`;
    
    previewThumbnailDOM.querySelector('img').src = frameFileTemplate;
    
    
    const previewThumbnailXY = {
        x : previewThumbnailDOM.clientWidth,
        y : previewThumbnailDOM.clientHeight,
    }

    previewThumbnailDOM.style.left = event.screenX - previewThumbnailXY.x - 60 + "px";
    previewThumbnailDOM.style.top = event.screenY - previewThumbnailXY.y - 200 + "px";
    previewThumbnailDOM.style.display = "block";

    
    
}

function videoDOMHandlerProgress() {
    const videoBufferedLastTimeRange = (videoDOM.buffered.length - 1);
    if (videoBufferedLastTimeRange < 0) return 0;

    const timeBuffered = videoDOM.buffered.end(videoBufferedLastTimeRange);
    return timeBuffered;
}

function VideoOnUpdateTime() {
    updateVideoPausePlayState();
    const bufferedTimeAll = videoDOM.duration;
    const bufferedTime = videoDOMHandlerProgress();
    seekbarTimersDOM.querySelector('#loaded').style.width = getPercentByNumber(bufferedTime, bufferedTimeAll) + "%";
    seekbarTimersDOM.querySelector('#progress').style.width = getPercentByNumber(videoDOM.currentTime, bufferedTimeAll) + "%";
    remainDOM.textContent = `- ${toMinutes(Math.round(videoDOM.duration - videoDOM.currentTime))}`;
}

function playpauseDOMHandlerClicked() {
    const thisElement = playpauseDOM;
    if (thisElement.classList.contains("pause")) {
        thisElement.classList.remove("pause");
        videoDOM.pause();
    } else {
        thisElement.classList.add("pause");
        videoDOM.play();
    }
}

function fullScreenDOMHandlerClicked() {
    if (videoDOM.requestFullscreen)
        videoDOM.requestFullscreen();
}

function windowHandlerExitFullScreen() {
    if (screen.height == window.innerHeight)
        if (document.exitFullscreen) {
            document.exitFullscreen();
        }
}

function windowHandlerShortcut(event){
    if(event.code == "Space"){
        playpauseDOMHandlerClicked();
    }

    if(event.code == "KeyF"){
        fullScreenDOMHandlerClicked();
    }
}

// helpers
function getPercentByNumber(piece, total) {
    const percent = piece / total * 100;
    return percent;
}

function getNumberByPercent(percent, total) {
    const number = percent * total / 100;
    return number;
}

function setInitialVideoOptions() {
    videoDOM.currentTime = 0;
    if (isAutoplay && isMuted) {
        playpauseDOM.classList.add("pause");
    }

    if (isMuted) {
        soundmuteDOM.classList.add("mute");
        seekbarVolumeDOM.classList.add("disabled");
    } else {
        videoDOM.volume = 0.5;
        document.querySelector('.sound #progress').style.width = '50%';
    }

    makePlaylistElementActive(videoPlaylistDOM);
}

function toMinutes(seconds) {
    let tmpSeconds = seconds;
    let minuteCounter = 0;
    let finallTime = "";
    while (true) {
        if (tmpSeconds < 60) {
            tmpSeconds = tmpSeconds.toString().padStart(2, '0');
            minuteCounter = minuteCounter.toString().padStart(2, '0');
            finallTime = `${minuteCounter}:${tmpSeconds}`;
        }
        tmpSeconds -= 60;
        minuteCounter++;
        if (tmpSeconds <= 0) break;
    }

    return finallTime;
}

function moveRangeSlider(element , event){
    const coordinate = element.getBoundingClientRect();
    const width = coordinate.width;
    const offset = {
        x: Math.round(coordinate.left),
        y: Math.round(coordinate.top),
    }
    const finallWidth = getPercentByNumber(Math.round(event.clientX - offset.x), width);
    return finallWidth;
}

function makePlaylistElementActive(elements){
    Array.from(elements).forEach((element)=>{
        element.classList.remove("active");
        if(decodeURI(videoDOM.src).search(element.dataset.src) != -1){
            element.classList.add("active");
        }
    });
}

function updateVideoPausePlayState(){
    if(videoDOM.paused){
        playpauseDOM.classList.remove("pause");
    }else{
        playpauseDOM.classList.add("pause");
    }
}


فایل main.css گرافیک سفارشی ویدیو پلیر

.container{
    margin: 0 auto;
    width: 80%;
    text-align: center;
    direction: rtl;
}

#introduce{
    display: block;
    width: 100%;
    font-size: 35px;
    font-weight: bold;
    color: white;
    padding-bottom: 5px;
    background-color: #4CAF50;
    text-decoration: none;
    margin-bottom: 15px;
}


/* Page Style */

.video-wrapper{
    position: relative;
}

.video-player-wrapper{
    position: relative;
    display: inline-block;
}

.video-controls{
    width: 100%;
    height: 50px;
    bottom: 4px;
    left: 0;
    position: absolute;
    background-image: linear-gradient(transparent,rgba(0,0,0,.75));
}

#my-custom-video-play{
    width: 720px;
    border-radius: 4px 0 0 4px;
    position: relative;
}

#my-custom-video-play::-webkit-media-controls{
    display:none !important;
}

#video-playlist{
    background-color: #ff9800;
    width: 190px;
    height: 405px;
    margin: 0;
    padding: 5px;
    box-sizing: border-box;
    list-style: none;
    position: absolute;
    right: 58px;
    overflow-y: auto;
}

#video-playlist li{
    text-align: right;
    cursor: pointer;
    margin-bottom: 10px;
    padding: 10px;
}

#video-playlist li:hover, #video-playlist li.active{
    background-color: cyan;
}

.icon-player{
    width: 18px;
    height: 18px;
    cursor: pointer;
    background-repeat: no-repeat;
    background-size: 18px;
    display: inline-block;
    position: absolute;
    bottom: 10px;
}

.icon-player.play-pause{
    background-image: url(../img/play-90.png);
    left: 10px;
}

.icon-player.play-pause.pause{
    background-image: url(../img/pause-90.png);
}

.icon-player.sound-mute{
    background-image: url(../img/sound-90.png);
    right: 140px;
}

.icon-player.sound-mute.mute{
    background-image: url(../img/mute-90.png);
}

.icon-player.full-screen{
    background-image: url(../img/full-screen-90.png);
    right: 10px;
}

.seekbar{
    background-color: rgb(255 255 255 / 22%);
    border-radius: 10px;
    height: 7px;
    bottom: 14px;
    position: absolute;
    cursor: pointer;
}

.seekbar #loaded{
    background-color: rgba(255, 255, 255, 0.726);
    border-radius: 10px;
    position: absolute;
    left: 0;
    bottom: 0;
    width: 1%;
    height: 7px;
    z-index: 5;
}

.seekbar #remain{
    cursor: default;
}

.seekbar #progress{
    background-color: #ff9800;
    border-radius: 10px;
    position: absolute;
    left: 0;
    bottom: 0;
    width: 0%;
    height: 7px;
    z-index: 10;
}

.seekbar #progress #handler{
    position: absolute;
    width: 14px;
    height: 14px;
    border-radius: 14px;
    background-color: #fff;
    bottom: -4px;
    right: -3px;
    z-index: 15;
}

.seekbar.timer{
    width: 455px;
    left: 40px;  
}

.seekbar.timer #remain{
    position: absolute;
    color: white;
    right: -50px;
    bottom: -5px;
    direction: ltr;
    -moz-user-select: -moz-none;
    -khtml-user-select: none;
    -webkit-user-select: none;
}

.seekbar.sound{
    width: 90px;
    right: 34px;
}

.seekbar.sound.disabled{
    cursor: not-allowed;
    opacity: 0.4;
}

.seekbar.sound #progress #handler{
    width: 4px;
}

.seekbar.sound #loaded{
    width: 100%;
}

#preview-thumbnail{
    width: 140px;
    position: absolute;
    left: 15px;
    top: 15px;
    padding: 15px;
    background-color: whitesmoke;
    border-radius: 8px;
    z-index: 20;
    display: none;
}

#preview-thumbnail img{
    width: 100%;
    border-radius: 8px;
}


دانلود ویدیو پلیر سفارشی با جاوا اسکریپت

ارسال نظر

جهت استفاده از کد حتما از تگ pre استفاده نمایید .

لیست نظرات

  1. مهدی
    مهدی

    سلام. میشه کاری کرد مسیر فایل روی سرور مخفی باشه یا کاری کرد که فیلم قابل دانلود نباشه؟

    07 دی 1401 | 09:23:33
  • حسین باقری
    حسین باقری

    درود ، میشه این کار با استفاده از ساخت encode انحصاری برای ویدیو انجام داد . اما مبحث پیشرفته ای هست و توی چند تا مقاله نمیشه توضیح داد .

    07 دی 1401 | 09:52:55
  • امیر محمد کلاری
    امیر محمد کلاری

    سلام وقتتون بخیر امکان استفاده از کد‌های مورد نظر برای وردپرس وجود داره؟

    29 خرداد 1401 | 17:33:43
    • حسین باقری
      حسین باقری

      سلام بله

      29 خرداد 1401 | 18:47:59
    contact us