<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Custom Video Player</title>
    <link rel="icon" type="image/png" href="favicon.png">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css"/>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <!-- Car Racing (1080P)-->
    <!-- https://pixabay.com/videos/download/video-41758_source.mp4?attachment -->
    <!-- Lake (4K) -->
    <!-- https://pixabay.com/videos/download/video-28745_source.mp4?attachment -->
    <!-- Ocean (720P)-->
    <!-- https://pixabay.com/videos/download/video-31377_tiny.mp4?attachment -->
    <div class="player">
        <video class="video" src='https://pixabay.com/videos/download/video-31377_tiny.mp4?attachment' playsinline></video>
        <div class="show-controls">
            <div class="controls-container">
                <!-- 진행률 -->
                <div class="progress-range" title="Seek">
                    <div class="progress-bar"></div>
                </div>
                <div class="control-group">
                    <!-- 좌측 조작 -->
                    <div class="controls-left">
                        <!-- 재생/멈춤 -->
                        <div class="play-controls">
                            <i class="fas fa-play" title="Play" id="play-btn"></i>
                        </div>
                        <!-- 음성 크기 -->
                        <div class="volume">
                            <div class="volume-icon">
                                <i class="fas fa-volume-up" title="Mute" id="volume-icon"></i>
                            </div>
                            <div class="volume-range" title="Change Volume">
                                <div class="volume-bar"></div>
                            </div>
                        </div>
                    </div>
                    <!-- 우측 조작. -->
                    <div class="controls-right">
                        <!-- 속도-->
                        <div class="speed" title="Playback Rate">
                            <select name="playbackRate" class="player-speed">
                                <option value="0.5">0.5 x</option>
                                <option value="0.75">0.75 x</option>
                                <option value="1" selected>1.0 x</option>
                                <option value="1.5">1.5 x</option>
                                <option value="2">2.0 x</option>
                            </select>
                        </div>
                        <!-- 시간 -->
                        <div class="time">
                            <span class="time-elapsed">00:00 / </span>
                            <span class="time-duration">02:49</span>
                        </div>
                        <!-- Fullscreen -->
                        <div class="fullscreen">
                            <i class="fas fa-expand"></i>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <!-- Script -->
    <script src="script.js"></script>
</body>
</html>
:root {
  --primary-color: dodgerblue;
  --font-color: white;
}

html {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  background-color: #e3e3e3;
  background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%234f4f51' fill-opacity='0.4' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E");
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: Helvetica, sans-serif;
}

.fas {
  color: var(--font-color);
  font-size: 35px;
}

.player {
  max-width: 80vw;
  min-width: 800px;
  border: 5px solid black;
  border-radius: 10px;
  background-color: black;
  position: relative;
  cursor: pointer;
}

video {
  border-radius: 5px;
  width: 100%;
  height: auto;
}

/* Containers */
.show-controls {
  width: 100%;
  height: 30%;
  z-index: 2;
  position: absolute;
  bottom: 0;
  cursor: default;
}

.controls-container {
  position: absolute;
  bottom: -5px;
  width: 100%;
  height: 95px;
  margin-top: -95px;
  background-color: rgba(0, 0, 0, 0.5);
  box-sizing: border-box;
  z-index: 5;
  display: flex;
  justify-content: space-between;
  opacity: 0;
  transition: all 0.5s ease-out 2s;
}

.show-controls:hover .controls-container {
  opacity: 1;
  transition: all 0.2s ease-out;
}

.control-group {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: space-between;
}

.controls-left,
.controls-right {
  flex: 1;
  display: flex;
  overflow: hidden;
  position: relative;
  top: 40px;
}

/* Progress Bar */
.progress-range {
  height: 8px;
  width: calc(100% - 30px);
  background: rgba(202, 202, 202, 0.5);
  margin: auto;
  border-radius: 10px;
  position: absolute;
  left: 15px;
  top: 15px;
  cursor: pointer;
  transition: height 0.1s ease-in-out;
  z-index: 10;
}

.progress-range:hover {
  height: 10px;
}

.progress-bar {
  background: var(--primary-color);
  width: 50%;
  height: 100%;
  border-radius: 10px;
  transition: all 0.5s ease;
}

/* Left Controls -------------------------- */

.controls-left {
  justify-content: flex-start;
  margin-left: 15px;
}

/* Play & Pause */
.play-controls {
  margin-right: 15px;
}

.fa-play:hover,
.fa-pause:hover {
  color: var(--primary-color);
  cursor: pointer;
}

/* Volume */
.volume-icon {
  cursor: pointer;
}

.volume-range {
  height: 8px;
  width: 100px;
  background: rgba(70, 70, 70, 0.5);
  border-radius: 10px;
  position: relative;
  top: -21px;
  left: 50px;
  cursor: pointer;
}

.volume-bar {
  background: var(--font-color);
  width: 100%;
  height: 100%;
  border-radius: 10px;
  transition: width 0.2s ease-in;
}

.volume-bar:hover {
  background: var(--primary-color);
}

/* Right Controls ---------------------------- */
.controls-right {
  justify-content: flex-end;
  margin-right: 15px;
}

.speed,
.time {
  position: relative;
  top: 10px;
}

/* Playback Speed */
.speed {
  margin-right: 15px;
}

select,
option {
  cursor: pointer;
}

select {
  appearance: none;
  background-color: transparent;
  color: var(--font-color);
  border: none;
  font-size: 18px;
  position: relative;
  top: -2.5px;
  border-radius: 5px;
}

select:focus {
  outline: none;
}

select > option {
  background-color: rgba(0, 0, 0, 0.9);
  border: none;
  font-size: 14px;
}

/* Elapsed Time & Duration */
.time {
  margin-right: 15px;
  color: var(--font-color);
  font-weight: bold;
  user-select: none;
}

/* Fullscreen */
.fullscreen {
  cursor: pointer;
}

.video-fullscreen {
  position: relative;
  top: 50%;
  transform: translateY(-50%);
}

/* Media Query: Large Smartphone (Vertical) */
@media screen and (max-width: 600px) {
  .player {
    min-width: 0;
    max-width: 95vw;
  }

  .fas {
    font-size: 20px;
  }

  .controls-container {
    height: 50px;
  }

  .control-group {
    position: relative;
    top: -25px;
  }

  .progress-range {
    width: 100%;
    top: 0;
    left: 0;
    border-radius: 0;
  }

  .progress-bar {
    border-radius: 0;
  }

  .volume-range {
    width: 50px;
    left: 30px;
    top: -15px;
  }

  .speed,
  .time {
    top: 3px;
  }

  select {
    font-size: 12px;
  }

  .time {
    font-size: 12px;
  }
}

/* Media Query: Large Smartphone (Horizontal) */
@media screen and (max-width: 900px) and (max-height: 500px) {
  .player {
    max-height: 95vh;
    max-width: auto;
  }

  video {
    height: 95vh;
    object-fit: cover;
  }

  .video-fullscreen {
    height: 97.5vh;
    border-radius: 0;
  }
}
//재생창 전체
const player = document.querySelector('.player');
//영상창 전체
const video = document.querySelector('.video');

//진행률 범위와 바.
const progressRange = document.querySelector('.progress-range');
const progressBar = document.querySelector('.progress-bar');


const playBtn = document.getElementById('play-btn');
const volumeIcon = document.getElementById('volume-icon');
const volumeRange = document.querySelector('.volume-range');
const volumeBar = document.querySelector('.volume-bar');
const speed = document.querySelector('.player-speed');
//현재시간
const currentTime = document.querySelector('.time-elapsed');
//전체 시간.
const duration = document.querySelector('.time-duration');
const fullscreenBtn = document.querySelector('.fullscreen');

// 재생 & 멈춤 ----------------------------------- //

function showPlayIcon() {
  playBtn.classList.replace('fa-pause', 'fa-play');
  playBtn.setAttribute('title', 'Play');
}
function togglePlay() {
  if (video.paused) {
    video.play();
    playBtn.classList.replace('fa-play', 'fa-pause');
    playBtn.setAttribute('title', 'Pause');
  } else {
    video.pause();
    showPlayIcon();
  }
  //멈추면 재생, 재생이면 멈춤.
  //css 디자인을 건드려주기.
}
// 영상 끝나면 영상 아이콘 보이게 하기.
video.addEventListener('ended', showPlayIcon);

// 진행률 바. ---------------------------------- //

// 현재 시간 기간을 계산해서 형식대로 반환해줌.
function displayTime(time) {
  const minutes = Math.floor(time / 60);
  let seconds = Math.floor(time % 60);
  seconds = seconds > 9 ? seconds : `0${seconds}`;
  return `${minutes}:${seconds}`;
}
// 영상 진행에 따라 진행률바 업데이트
function updateProgress() {
  progressBar.style.width = `${(video.currentTime / video.duration) * 100}%`;
  currentTime.textContent = `${displayTime(video.currentTime)} /`;
  duration.textContent = `${displayTime(video.duration)}`;
}
//클릭으로 비디오 진행.
function setProgress(e) {
  const newTime = e.offsetX / progressRange.offsetWidth;
  progressBar.style.width = `${newTime * 100}%`;
  video.currentTime = newTime * video.duration;
}

// 음성 조작. --------------------------- //

//변하는 음성값을 최종적으로 저장하여 보존하는 음성값.
let lastVolume = 1;

// 음소거.
function toggleMute() {
  //기본 리셋
  volumeIcon.className = '';
  if (video.volume) {
    //음성의 변경값이 있을때
    lastVolume = video.volume;
    video.volume = 0;
    volumeIcon.classList.add('fas', 'fa-volume-mute');
    volumeIcon.setAttribute('title', 'Unmute');
    volumeBar.style.width = 0;
  } else {
    video.volume = lastVolume;
    volumeIcon.classList.add('fas', 'fa-volume-up');
    volumeIcon.setAttribute('title', 'Mute');
    volumeBar.style.width = `${lastVolume * 100}%`;
  }
}

// 음성 
function changeVolume(e) {
  //누른 위치/ 전체 음성 길이는 현재 음성 크기.
  let volume = e.offsetX / volumeRange.offsetWidth;
  // 볼륨이 최대최소 근사하면 그 값으로 
  if (volume < 0.1) {
    volume = 0;
  }
  if (volume > 0.9) {
    volume = 1;
  }
  volumeBar.style.width = `${volume * 100}%`;
  video.volume = volume;

  // 음성 크기에 따라 아이콘 변경.
  volumeIcon.className = '';
  if (volume > 0.7) {
    volumeIcon.classList.add('fas', 'fa-volume-up');
  } else if (volume < 0.7 && volume > 0) {
    volumeIcon.classList.add('fas', 'fa-volume-down');
  } else if (volume === 0) {
    volumeIcon.classList.add('fas', 'fa-volume-off');
  }
  lastVolume = volume;
  //최종 음성 값에 저장.
}

// 영상재생속도 변경. -------------------- //

function changeSpeed() {
  video.playbackRate = speed.value;
}

// 전체화면. ------------------------------- //

/* 전체화면으로 보기 */
function openFullscreen(element) {
  if (element.requestFullscreen) {
    element.requestFullscreen();
  } else if (element.mozRequestFullScreen) {
    /* Firefox */
    element.mozRequestFullScreen();
  } else if (element.webkitRequestFullscreen) {
    /* Chrome, Safari and Opera */
    element.webkitRequestFullscreen();
  } else if (element.msRequestFullscreen) {
    /* IE/Edge */
    element.msRequestFullscreen();
  }
  video.classList.add('video-fullscreen');
}

/* 전체화면 닫기. */
function closeFullscreen() {
  if (document.exitFullscreen) {
    document.exitFullscreen();
  } else if (document.mozCancelFullScreen) {
    /* Firefox */
    document.mozCancelFullScreen();
  } else if (document.webkitExitFullscreen) {
    /* Chrome, Safari and Opera */
    document.webkitExitFullscreen();
  } else if (document.msExitFullscreen) {
    /* IE/Edge */
    document.msExitFullscreen();
  }
  video.classList.remove('video-fullscreen');
}

let fullscreen = false;

// 전체화면 토글
function toggleFullscreen() {
  if (!fullscreen) {
    openFullscreen(player);
  } else {
    closeFullscreen();
  }
  fullscreen = !fullscreen;
}

// 이벤트 리스너.
playBtn.addEventListener('click', togglePlay);
video.addEventListener('click', togglePlay);
video.addEventListener('timeupdate', updateProgress);
video.addEventListener('canplay', updateProgress);
progressRange.addEventListener('click', setProgress);
volumeRange.addEventListener('click', changeVolume);
volumeIcon.addEventListener('click', toggleMute);
speed.addEventListener('change', changeSpeed);
fullscreenBtn.addEventListener('click', toggleFullscreen);
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.