<!-- These 3 links are only for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<div class="container show-controls">
<div class="wrapper">
<div class="video-timeline">
<div class="progress-area">
<span>00:00</span>
<div class="progress-bar"></div>
</div>
</div>
<ul class="video-controls">
<li class="options left">
<button class="volume"><i class="fa-solid fa-volume-high"></i></button>
<input type="range" min="0" max="1" step="any">
<div class="video-timer">
<p class="current-time">00:00</p>
<p class="separator"> / </p>
<p class="video-duration">00:00</p>
</div>
</li>
<li class="options center">
<button class="skip-backward"><i class="fas fa-backward"></i></button>
<button class="play-pause"><i class="fas fa-play"></i></button>
<button class="skip-forward"><i class="fas fa-forward"></i></button>
</li>
<li class="options right">
<div class="playback-content">
<button class="playback-speed"><span class="material-symbols-rounded">slow_motion_video</span></button>
<ul class="speed-options">
<li data-speed="2">2x</li>
<li data-speed="1.5">1.5x</li>
<li data-speed="1" class="active">Normal</li>
<li data-speed="0.75">0.75x</li>
<li data-speed="0.5">0.5x</li>
</ul>
</div>
<button class="pic-in-pic"><span class="material-icons">picture_in_picture_alt</span></button>
<button class="fullscreen"><i class="fa-solid fa-expand"></i></button>
</li>
</ul>
</div>
<video width="100%" controls>
<source src="#videourl" type="video/mp4">
Your browser does not support HTML video.
</video>
</div>
<script src="script.js"></script>
<style>
/* Import Google font - Poppins */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');
*{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body{
min-height: 100vh;
background: #E3F2FD;
}
body, .container, .video-controls, .video-timer, .options{
display: flex;
align-items: center;
justify-content: center;
}
.container{
width: 98%;
user-select: none;
overflow: hidden;
max-width: 900px;
border-radius: 5px;
background: #000;
aspect-ratio: 16 / 9;
position: relative;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}
.container.fullscreen{
max-width: 100%;
width: 100%;
height: 100vh;
border-radius: 0px;
}
.wrapper{
position: absolute;
left: 0;
right: 0;
z-index: 1;
opacity: 0;
bottom: -15px;
transition: all 0.08s ease;
}
.container.show-controls .wrapper{
opacity: 1;
bottom: 0;
transition: all 0.13s ease;
}
.wrapper::before{
content: "";
bottom: 0;
width: 100%;
z-index: -1;
position: absolute;
height: calc(100% + 35px);
pointer-events: none;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
}
.video-timeline{
height: 7px;
width: 100%;
cursor: pointer;
}
.video-timeline .progress-area{
height: 3px;
position: relative;
background: rgba(255, 255, 255, 0.6);
}
.progress-area span{
position: absolute;
left: 50%;
top: -25px;
font-size: 13px;
color: #fff;
pointer-events: none;
transform: translateX(-50%);
}
.progress-area .progress-bar{
width: 0%;
height: 100%;
position: relative;
background: #2289ff;
}
.progress-bar::before{
content: "";
right: 0;
top: 50%;
height: 13px;
width: 13px;
position: absolute;
border-radius: 50%;
background: #2289ff;
transform: translateY(-50%);
}
.progress-bar::before, .progress-area span{
display: none;
}
.video-timeline:hover .progress-bar::before,
.video-timeline:hover .progress-area span{
display: block;
}
.wrapper .video-controls{
padding: 5px 20px 10px;
}
.video-controls .options{
width: 100%;
}
.video-controls .options:first-child{
justify-content: flex-start;
}
.video-controls .options:last-child{
justify-content: flex-end;
}
.options button{
height: 40px;
width: 40px;
font-size: 19px;
border: none;
cursor: pointer;
background: none;
color: #efefef;
border-radius: 3px;
transition: all 0.3s ease;
}
.options button :where(i, span) {
height: 100%;
width: 100%;
line-height: 40px;
}
.options button:hover :where(i, span){
color: #fff;
}
.options button:active :where(i, span){
transform: scale(0.9);
}
.options button span{
font-size: 23px;
}
.options input{
height: 4px;
margin-left: 3px;
max-width: 75px;
accent-color: #0078FF;
}
.options .video-timer{
color: #efefef;
margin-left: 15px;
font-size: 14px;
}
.video-timer .separator{
margin: 0 5px;
font-size: 16px;
font-family: "Open sans";
}
.playback-content{
display: flex;
position: relative;
}
.playback-content .speed-options{
position: absolute;
list-style: none;
left: -40px;
bottom: 40px;
width: 95px;
overflow: hidden;
opacity: 0;
border-radius: 4px;
pointer-events: none;
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
transition: opacity 0.13s ease;
}
.playback-content .speed-options.show{
opacity: 1;
pointer-events: auto;
}
.speed-options li{
cursor: pointer;
color: #000;
font-size: 14px;
margin: 2px 0;
padding: 5px 0 5px 15px;
transition: all 0.1s ease;
}
.speed-options li:where(:first-child, :last-child){
margin: 0px;
}
.speed-options li:hover{
background: #dfdfdf;
}
.speed-options li.active{
color: #fff;
background: #3e97fd;
}
.container video{
width: 100%;
}
@media screen and (max-width: 540px) {
.wrapper .video-controls{
padding: 3px 10px 7px;
}
.options input, .progress-area span{
display: none!important;
}
.options button{
height: 30px;
width: 30px;
font-size: 17px;
}
.options .video-timer{
margin-left: 5px;
}
.video-timer .separator{
font-size: 14px;
margin: 0 2px;
}
.options button :where(i, span) {
line-height: 30px;
}
.options button span{
font-size: 21px;
}
.options .video-timer, .progress-area span, .speed-options li{
font-size: 12px;
}
.playback-content .speed-options{
width: 75px;
left: -30px;
bottom: 30px;
}
.speed-options li{
margin: 1px 0;
padding: 3px 0 3px 10px;
}
.right .pic-in-pic{
display: none;
}
}
</style>
<script>
const container = document.querySelector(".container"),
mainVideo = container.querySelector("video"),
videoTimeline = container.querySelector(".video-timeline"),
progressBar = container.querySelector(".progress-bar"),
volumeBtn = container.querySelector(".volume i"),
volumeSlider = container.querySelector(".left input");
currentVidTime = container.querySelector(".current-time"),
videoDuration = container.querySelector(".video-duration"),
skipBackward = container.querySelector(".skip-backward i"),
skipForward = container.querySelector(".skip-forward i"),
playPauseBtn = container.querySelector(".play-pause i"),
speedBtn = container.querySelector(".playback-speed span"),
speedOptions = container.querySelector(".speed-options"),
pipBtn = container.querySelector(".pic-in-pic span"),
fullScreenBtn = container.querySelector(".fullscreen i");
let timer;
const hideControls = () => {
if(mainVideo.paused) return;
timer = setTimeout(() => {
container.classList.remove("show-controls");
}, 3000);
}
hideControls();
container.addEventListener("mousemove", () => {
container.classList.add("show-controls");
clearTimeout(timer);
hideControls();
});
const formatTime = time => {
let seconds = Math.floor(time % 60),
minutes = Math.floor(time / 60) % 60,
hours = Math.floor(time / 3600);
seconds = seconds < 10 ? `0${seconds}` : seconds;
minutes = minutes < 10 ? `0${minutes}` : minutes;
hours = hours < 10 ? `0${hours}` : hours;
if(hours == 0) {
return `${minutes}:${seconds}`
}
return `${hours}:${minutes}:${seconds}`;
}
videoTimeline.addEventListener("mousemove", e => {
let timelineWidth = videoTimeline.clientWidth;
let offsetX = e.offsetX;
let percent = Math.floor((offsetX / timelineWidth) * mainVideo.duration);
const progressTime = videoTimeline.querySelector("span");
offsetX = offsetX < 20 ? 20 : (offsetX > timelineWidth - 20) ? timelineWidth - 20 : offsetX;
progressTime.style.left = `${offsetX}px`;
progressTime.innerText = formatTime(percent);
});
videoTimeline.addEventListener("click", e => {
let timelineWidth = videoTimeline.clientWidth;
mainVideo.currentTime = (e.offsetX / timelineWidth) * mainVideo.duration;
});
mainVideo.addEventListener("timeupdate", e => {
let {currentTime, duration} = e.target;
let percent = (currentTime / duration) * 100;
progressBar.style.width = `${percent}%`;
currentVidTime.innerText = formatTime(currentTime);
});
mainVideo.addEventListener("loadeddata", () => {
videoDuration.innerText = formatTime(mainVideo.duration);
});
const draggableProgressBar = e => {
let timelineWidth = videoTimeline.clientWidth;
progressBar.style.width = `${e.offsetX}px`;
mainVideo.currentTime = (e.offsetX / timelineWidth) * mainVideo.duration;
currentVidTime.innerText = formatTime(mainVideo.currentTime);
}
volumeBtn.addEventListener("click", () => {
if(!volumeBtn.classList.contains("fa-volume-high")) {
mainVideo.volume = 0.5;
volumeBtn.classList.replace("fa-volume-xmark", "fa-volume-high");
} else {
mainVideo.volume = 0.0;
volumeBtn.classList.replace("fa-volume-high", "fa-volume-xmark");
}
volumeSlider.value = mainVideo.volume;
});
volumeSlider.addEventListener("input", e => {
mainVideo.volume = e.target.value;
if(e.target.value == 0) {
return volumeBtn.classList.replace("fa-volume-high", "fa-volume-xmark");
}
volumeBtn.classList.replace("fa-volume-xmark", "fa-volume-high");
});
speedOptions.querySelectorAll("li").forEach(option => {
option.addEventListener("click", () => {
mainVideo.playbackRate = option.dataset.speed;
speedOptions.querySelector(".active").classList.remove("active");
option.classList.add("active");
});
});
document.addEventListener("click", e => {
if(e.target.tagName !== "SPAN" || e.target.className !== "material-symbols-rounded") {
speedOptions.classList.remove("show");
}
});
fullScreenBtn.addEventListener("click", () => {
container.classList.toggle("fullscreen");
if(document.fullscreenElement) {
fullScreenBtn.classList.replace("fa-compress", "fa-expand");
return document.exitFullscreen();
}
fullScreenBtn.classList.replace("fa-expand", "fa-compress");
container.requestFullscreen();
});
speedBtn.addEventListener("click", () => speedOptions.classList.toggle("show"));
pipBtn.addEventListener("click", () => mainVideo.requestPictureInPicture());
skipBackward.addEventListener("click", () => mainVideo.currentTime -= 5);
skipForward.addEventListener("click", () => mainVideo.currentTime += 5);
mainVideo.addEventListener("play", () => playPauseBtn.classList.replace("fa-play", "fa-pause"));
mainVideo.addEventListener("pause", () => playPauseBtn.classList.replace("fa-pause", "fa-play"));
playPauseBtn.addEventListener("click", () => mainVideo.paused ? mainVideo.play() : mainVideo.pause());
videoTimeline.addEventListener("mousedown", () => videoTimeline.addEventListener("mousemove", draggableProgressBar));
document.addEventListener("mouseup", () => videoTimeline.removeEventListener("mousemove", draggableProgressBar));
</script>
Contact Us!
0 Comments
type your comment...