diff --git a/src/functions/buttons.js b/src/functions/buttons.js new file mode 100644 index 0000000..6758249 --- /dev/null +++ b/src/functions/buttons.js @@ -0,0 +1,215 @@ +const video = document.querySelector('.plyx-player') +const videoContainer = document.querySelector('.video-container') + +function AddControls() { + var add_controls = ` +
+
+
+ + + + +
+ +
+
+
+
+ + / + +
+ +
+
+
+
+ + +
00:00
+
+
+
+ ` + video.insertAdjacentHTML("afterend", add_controls); +} + +function Util() { + const videoControls = document.querySelector('.plyx-player-controls') + // Play/Plause + const Button_PlayPause = document.querySelector('.plyx-player-controls #play-pause') + + Button_PlayPause.addEventListener('click', Toggle_PlayPause) + video.addEventListener('click', Toggle_PlayPause) + video.addEventListener('play', Update_PlayPauseButton) + video.addEventListener('pause', Update_PlayPauseButton) + + function Toggle_PlayPause() { + if (video.paused || video.ended) { + video.play() + } + else { + video.pause() + } + } + + function Update_PlayPauseButton() { + if (video.paused) { + Button_PlayPause.setAttribute('data-title', 'Play (k)') + Button_PlayPause.innerHTML = `` + } else { + Button_PlayPause.setAttribute('data-title', 'Pause (k)') + Button_PlayPause.innerHTML = `` + } + } + + // Fullscreen + const Button_Fullscreen = document.getElementById('fullscreen'); + function Toggle_Fullscreen() { + if (document.fullscreenElement) {document.exitFullscreen()} + else if (document.webkitFullscreenElement) {document.webkitExitFullscreen()} + else if (videoContainer.webkitRequestFullscreen) {videoContainer.webkitRequestFullscreen()} + else {videoContainer.requestFullscreen()} + } + + Button_Fullscreen.onclick = Toggle_Fullscreen; + function Update_FullscreenButton() { + if (document.fullscreenElement) { + Button_Fullscreen.setAttribute('data-title', 'Exit full screen (f)') + Button_Fullscreen.innerHTML = `` + } else { + Button_Fullscreen.setAttribute('data-title', 'Full screen (f)') + Button_Fullscreen.innerHTML = `` + } + } + + videoContainer.addEventListener('fullscreenchange', Update_FullscreenButton); + + // Skip Back or Forth + const Button_SkipBack = document.querySelector('.plyx-player-controls #skip-back') + const Button_SkipForth = document.querySelector('.plyx-player-controls #skip-forth') + + Button_SkipBack.addEventListener('click', Toggle_SkipBack) + Button_SkipForth.addEventListener('click', Toggle_SkipForth) + + function Toggle_SkipBack() {Skip(-10)} + function Toggle_SkipForth() {Skip(10)} + + function Skip(value) { + video.currentTime += value; + } + + // Volume (Also the slider) + const Button_Volume = document.getElementById('volume-button') + const volume = document.getElementById('volume') + function Update_Volme() { + if (video.muted) { + video.muted = false + } + video.volume = volume.value + } + + volume.addEventListener('input', Update_Volme) + + function Update_Volume_Icon() { + Button_Volume.setAttribute('data-title', 'Mute (m)') + + if (video.muted || video.volume === 0) { + Button_Volume.innerHTML = `` + Button_Volume.setAttribute('data-title', 'Unmute (m)') + } else if (video.volume > 0 && video.volume <= 0.5) { + Button_Volume.innerHTML = `` + } else { + Button_Volume.innerHTML = `` + } + } + + video.addEventListener('volumechange', Update_Volume_Icon) + + function Toggle_Mute() { + video.muted = !video.muted; + + if (video.muted) { + volume.setAttribute('data-volume', volume.value) + volume.value = 0; + } else { + volume.value = volume.dataset.volume + } + } + Button_Volume.addEventListener('click', Toggle_Mute) + + // Keyboard Shortcuts + function keyboardShortcuts(event) { + const { key } = event; + switch(key) { + case 'k': + Toggle_PlayPause(); + if (video.paused) { + Show_Controls(); + } else { + setTimeout(() => { + Hide_Controls(); + }, 2000); + } + break; + case 'm': + Toggle_Mute(); + break; + case 'f': + Toggle_Fullscreen(); + break; + case 'j': + Toggle_SkipBack(); + break; + case 'l': + Toggle_SkipForth(); + break; + } + } + document.addEventListener('keyup', keyboardShortcuts); + + // Auto Hide Controls + function Hide_Controls() { + if (video.paused) { + return + } + + videoControls.classList.add('hide') + console.log('0') + } + + // Show_Controls displays the video controls + function Show_Controls() { + videoControls.classList.remove('hide') + console.log('1') + } + video.addEventListener('mouseenter', Show_Controls) + video.addEventListener('mouseleave', Hide_Controls) + videoControls.addEventListener('mouseenter', Show_Controls) + videoControls.addEventListener('mouseleave', Hide_Controls) + + var mouseTimer = null, cursorVisible = true + + function disappearCursor() { + mouseTimer = null + document.body.style.cursor = "none" + cursorVisible = false + Hide_Controls() + } + + document.onmousemove = function() { + if (mouseTimer) { + window.clearTimeout(mouseTimer); + Show_Controls(); + } + if (!cursorVisible) { + document.body.style.cursor = "default" + cursorVisible = true + } + mouseTimer = window.setTimeout(disappearCursor, 1500) + } +} + + +export {AddControls, Util} \ No newline at end of file diff --git a/src/functions/length.js b/src/functions/length.js new file mode 100644 index 0000000..ba4bd83 --- /dev/null +++ b/src/functions/length.js @@ -0,0 +1,75 @@ +const video = document.querySelector('.plyx-player') + +function VideoLength() { + // Duration and Length of Video + const timeElapsed = document.getElementById('time-elapsed'); + const duration = document.getElementById('duration'); + + function formatTime(timeInSeconds) { + const result = new Date(timeInSeconds * 1000).toISOString().substr(11, 8); + + return { + minutes: result.substr(3, 2), + seconds: result.substr(6, 2), + }; + }; + + + function initializeVideo() { + const videoDuration = Math.round(video.duration); + const time = formatTime(videoDuration); + duration.innerText = `${time.minutes}:${time.seconds}`; + duration.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`); + } + video.addEventListener('loadedmetadata', initializeVideo); + function updateTimeElapsed() { + const time = formatTime(Math.round(video.currentTime)); + timeElapsed.innerText = `${time.minutes}:${time.seconds}`; + timeElapsed.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`); + } + video.addEventListener('timeupdate', updateTimeElapsed); + + // Progress Bar + const progressBar = document.getElementById('progress-bar'); + const seek = document.getElementById('seek'); + function initializeVideo() { + const videoDuration = Math.round(video.duration); + seek.setAttribute('max', videoDuration); + progressBar.setAttribute('max', videoDuration); + const time = formatTime(videoDuration); + duration.innerText = `${time.minutes}:${time.seconds}`; + duration.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`) + } + + function updateProgress() { + seek.value = Math.floor(video.currentTime); + progressBar.value = Math.floor(video.currentTime); + } + + video.addEventListener('timeupdate', updateProgress); + + const seekTooltip = document.getElementById('seek-tooltip'); + + function updateSeekTooltip(event) { + const skipTo = Math.round((event.offsetX / event.target.clientWidth) * parseInt(event.target.getAttribute('max'), 10)); + seek.setAttribute('data-seek', skipTo) + const t = formatTime(skipTo); + seekTooltip.textContent = `${t.minutes}:${t.seconds}`; + const rect = video.getBoundingClientRect(); + seekTooltip.style.left = `${event.pageX - rect.left}px`; + } + + seek.addEventListener('mousemove', updateSeekTooltip); + + function skipAhead(event) { + const skipTo = event.target.dataset.seek ? event.target.dataset.seek : event.target.value; + video.currentTime = skipTo; + progressBar.value = skipTo; + seek.value = skipTo; + } + seek.addEventListener('input', skipAhead); + + initializeVideo() +} + +export {VideoLength} \ No newline at end of file diff --git a/src/functions/progress-bar.js b/src/functions/progress-bar.js new file mode 100644 index 0000000..e69de29 diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..8c85086 --- /dev/null +++ b/src/index.js @@ -0,0 +1,8 @@ +import {Util, AddControls} from './functions/buttons.js' +import {VideoLength} from './functions/length.js' + +AddControls() +setTimeout(() => { + Util() + VideoLength() +}, 0o100); \ No newline at end of file diff --git a/src/styles/app.css b/src/styles/app.css new file mode 100644 index 0000000..ddf6b43 --- /dev/null +++ b/src/styles/app.css @@ -0,0 +1,181 @@ +.plyx-player { + width: 100%; + display: inline-flex; +} +.video-container { + position: relative; + display: flex; + flex-direction: column; + justify-content: center; +} +.plyx-player-controls { + display: inline-flex; + right: 0; + left: 0; + padding: 10px; + position: absolute; + bottom: 0; + transition: all 0.2s ease; + background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5)); + flex-direction: inherit; +} +.row-1 { + display: flex; + justify-content: space-between; + width: 100%; +} +.row-1-start { + display: flex; + align-items: center; +} +.row-1-end { + display: flex; + align-items: center; +} +button { + aspect-ratio: 1; + height: 32px; + width: 32px; + color: white; + background-color: transparent; + border: none; + margin: 0px 6px; +} +button:hover { + background: rgba(44, 44, 44, 0.6); + border-radius: 6px; +} +#volume-button svg { + aspect-ratio: 1; + height: 32px; + width: 32px; + fill: white; + padding: 3px 0px 0px 0px; +} +.video-controls.hide { + opacity: 0; + pointer-events: none; +} +.video-progress { + position: relative; + height: 6.4px; + margin: 6px -10px -8px -10px; +} +progress { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: 2px; + width: 100%; + height: 8.4px; + pointer-events: none; + position: absolute; + top: 0; +} +progress::-webkit-progress-bar { + background-color: #474545; + border-radius: 2px; +} +progress::-webkit-progress-value { + background: red; + border-radius: 2px; +} +progress::-moz-progress-bar { + border: none; + background: #ff7e7e; +} +.seek { + position: absolute; + top: 0; + width: 100%; + cursor: pointer; + margin: 0; +} +.seek:hover + .seek-tooltip { + display: block; +} +.seek-tooltip { + display: none; + position: absolute; + top: -50px; + margin-left: -20px; + font-size: 12px; + padding: 3px; + content: attr(data-title); + font-weight: bold; + color: #fff; + background-color: rgba(0, 0, 0, 0.6); +} +input[type=range] { + -webkit-appearance: none; + -moz-appearance: none; + height: 8.4px; + background: transparent; + cursor: pointer; +} +input[type=range]:focus { + outline: none; +} +input[type=range]:focus::-webkit-slider-runnable-track { + background: transparent; +} +input[type=range]:focus::-moz-range-track { + outline: none; +} +input[type=range]::-webkit-slider-runnable-track { + width: 100%; + cursor: pointer; + border-radius: 1.3px; + -webkit-appearance: none; + transition: all 0.4s ease; +} +input[type=range]::-webkit-slider-thumb { + height: 16px; + width: 16px; + border-radius: 16px; + background: red; + cursor: pointer; + -webkit-appearance: none; + margin-left: -1px; +} +input[type=range]::-moz-range-track { + width: 100%; + height: 8.4px; + cursor: pointer; + border: 1px solid transparent; + background: transparent; + border-radius: 0; +} +input[type=range].volume { + height: 5px; + background-color: #fff; +} +input[type=range].volume::-webkit-slider-runnable-track { + background-color: transparent; +} +input[type=range].volume::-webkit-slider-thumb { + margin-left: 0; + height: 14px; + width: 14px; + background: #fff; +} +input[type=range].volume::-moz-range-thumb { + border: 1px solid #fff; + background: #fff; +} +input[type="range"]::-moz-range-thumb { + height: 12px; + width: 6px; + border-radius: 0px; + border: none; + background: #f00; + cursor: pointer; +} +.hide { + opacity: 0; + pointer-events: none; +} +#progress-bar { + background: transparent; + border: none; +}