<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);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.