index.html
· 5.8 KiB · HTML
Raw
<video controls style="width: 100%; margin-bottom: 24px;" src="./vid.mp4"></video>
<div class="full-seekbar">
<div class="video-timestamp">
<span id="text-live">--:--</span>
<span id="text-full">--:--</span>
</div>
<div class="seekbar">
<span class="seekbar-current-progress"></span>
<span class="seekbar-tooltip"></span>
<input id="seek" name="seekbar" type="range"/>
</div>
</div>
<style>
body {
font-family: arial,sans-serif;
}
.full-seekbar {
display: flex;
flex-direction: row-reverse;
align-items: center;
gap: 6px;
}
.video-timestamp {
background: gray;
border-radius: 3rem;
color: white;
display: flex;
flex-direction: row;
align-items: center;
width: min-content;
padding: 4px 12px 4px 4px;
font-size: 14px;
gap: 6px;
}
#text-live {
width: max-content;
background: #414141;
border-radius: 3rem;
padding: 4px 12px;
}
.seekbar {
width: 100%;
position: relative;
background: gray;
border-radius: 3rem;
height: 6px;
display: flex;
align-items: center;
}
.seekbar input {
position: absolute;
left: 0;
width: 100%;
accent-color: red;
opacity: 0;
}
.seekbar-tooltip {
position: absolute;
background: #d3d3d3;
color: black;
border-radius: 3rem;
transform: translate(-24px, 0px);
padding: 4px 8px;
font-size: 12px;
border: 6px white solid;
transition: 0.3s font-size;
}
.seekbar-current-progress {
background: red;
border-radius: 3rem;
height: 6px;
display: flex;
align-items: center;
}
</style>
<script>
// Set variables
const VideoElement = document.querySelector('video')
const SeekbarTooltip = document.querySelector('.seekbar-tooltip')
const SeekbarElementInput = document.querySelector('#seek')
const SeekbarProgressbarElement = document.querySelector('.seekbar .seekbar-current-progress')
////////////////////////////////
// Video Seekbar and Tooltip //
////////////////////////////////
// Set max attribute to the range input
VideoElement.addEventListener('loadedmetadata', () => {
const VideoMaxLength = Math.round(VideoElement.duration).toString()
SeekbarElementInput.setAttribute("max", VideoMaxLength)
})
// Update progress bar and live timestamp
function UpdateSeekbar() {
SeekbarElementInput.value = Math.floor(VideoElement.currentTime)
SeekbarProgressbarElement.style.width = VideoElement.currentTime / VideoElement.duration * 100 + '%'
SeekbarTooltip.style.left = VideoElement.currentTime / VideoElement.duration * 100 + '%'
}
// Format timestamp for Seekbar Tooltip
function TooltipFormat(Second) {
const result = new Date(Second * 1e3).toISOString().slice(11, 19)
return {
hours: result.slice(0, 2),
minutes: result.slice(3, 5),
seconds: result.slice(6, 8),
}
}
// Tooltip
function updateSeekTooltip(event) {
const skipTo = Math.round(
(event.offsetX / event.target.clientWidth) *
parseInt(event.target.getAttribute("max"), 10),
)
SeekbarElementInput.setAttribute("data-seek", skipTo.toString())
const t = TooltipFormat(skipTo)
if (t.hours === "00") {SeekbarTooltip.textContent = `${t.minutes}:${t.seconds}`}
else {SeekbarTooltip.textContent = `${t.hours}:${t.minutes}:${t.seconds}`}
const rect = SeekbarProgressbarElement.getBoundingClientRect()
SeekbarTooltip.style.left = `${event.pageX - rect.left}px`
SeekbarTooltip.style.fontSize = '12'
SeekbarElementInput.addEventListener('mouseleave', () => {
SeekbarTooltip.style.fontSize = '0'
SeekbarTooltip.style.left = VideoElement.currentTime / VideoElement.duration * 100 + '%'
})
}
// If end-users clicks anywhere on the seekbar, skip video current time to the point
SeekbarElementInput.addEventListener("mousemove", updateSeekTooltip)
function skipAhead(event) {
const skipTo = event.target.dataset.seek ? event.target.dataset.seek : event.target.value
VideoElement.currentTime = skipTo
SeekbarProgressbarElement.value = skipTo
SeekbarElementInput.value = skipTo
}
SeekbarElementInput.addEventListener("input", skipAhead)
///////////////////////////////////
// Video Duration and Timestamp //
///////////////////////////////////
// Convert the video duration to a readable format
Number.prototype.VideoReadableFormat = function () {
let seconds = Math.floor(this), hours = Math.floor(seconds / 3600)
seconds -= hours*3600
let minutes = Math.floor(seconds / 60)
seconds -= minutes*60
if (hours < 10) {hours = "0"+hours}
if (minutes < 10) {minutes = "0"+minutes}
if (seconds < 10) {seconds = "0"+seconds}
if (hours === '00') {
// Hide 'hour' if the video not over an hour long
return minutes+':'+seconds
} else {
return hours+':'+minutes+':'+seconds
}
}
// When the video is playing, automatically update the 'Current Time' text
VideoElement.addEventListener('timeupdate', Live)
VideoElement.addEventListener('timeupdate', UpdateSeekbar)
function Live() {document.getElementById('text-live').innerText = `${VideoElement.currentTime.VideoReadableFormat()}`}
// Post the video's full duration
VideoElement.onloadedmetadata = function() {document.getElementById('text-full').innerText = `${VideoElement.duration.VideoReadableFormat()}`}
</script>
| 1 | <video controls style="width: 100%; margin-bottom: 24px;" src="./vid.mp4"></video> |
| 2 | <div class="full-seekbar"> |
| 3 | <div class="video-timestamp"> |
| 4 | <span id="text-live">--:--</span> |
| 5 | <span id="text-full">--:--</span> |
| 6 | </div> |
| 7 | <div class="seekbar"> |
| 8 | <span class="seekbar-current-progress"></span> |
| 9 | <span class="seekbar-tooltip"></span> |
| 10 | <input id="seek" name="seekbar" type="range"/> |
| 11 | </div> |
| 12 | </div> |
| 13 | |
| 14 | <style> |
| 15 | body { |
| 16 | font-family: arial,sans-serif; |
| 17 | } |
| 18 | .full-seekbar { |
| 19 | display: flex; |
| 20 | flex-direction: row-reverse; |
| 21 | align-items: center; |
| 22 | gap: 6px; |
| 23 | } |
| 24 | .video-timestamp { |
| 25 | background: gray; |
| 26 | border-radius: 3rem; |
| 27 | color: white; |
| 28 | display: flex; |
| 29 | flex-direction: row; |
| 30 | align-items: center; |
| 31 | width: min-content; |
| 32 | padding: 4px 12px 4px 4px; |
| 33 | font-size: 14px; |
| 34 | gap: 6px; |
| 35 | } |
| 36 | #text-live { |
| 37 | width: max-content; |
| 38 | background: #414141; |
| 39 | border-radius: 3rem; |
| 40 | padding: 4px 12px; |
| 41 | } |
| 42 | .seekbar { |
| 43 | width: 100%; |
| 44 | position: relative; |
| 45 | background: gray; |
| 46 | border-radius: 3rem; |
| 47 | height: 6px; |
| 48 | display: flex; |
| 49 | align-items: center; |
| 50 | } |
| 51 | .seekbar input { |
| 52 | position: absolute; |
| 53 | left: 0; |
| 54 | width: 100%; |
| 55 | accent-color: red; |
| 56 | opacity: 0; |
| 57 | } |
| 58 | .seekbar-tooltip { |
| 59 | position: absolute; |
| 60 | background: #d3d3d3; |
| 61 | color: black; |
| 62 | border-radius: 3rem; |
| 63 | transform: translate(-24px, 0px); |
| 64 | padding: 4px 8px; |
| 65 | font-size: 12px; |
| 66 | border: 6px white solid; |
| 67 | transition: 0.3s font-size; |
| 68 | } |
| 69 | .seekbar-current-progress { |
| 70 | background: red; |
| 71 | border-radius: 3rem; |
| 72 | height: 6px; |
| 73 | display: flex; |
| 74 | align-items: center; |
| 75 | } |
| 76 | </style> |
| 77 | |
| 78 | <script> |
| 79 | // Set variables |
| 80 | const VideoElement = document.querySelector('video') |
| 81 | const SeekbarTooltip = document.querySelector('.seekbar-tooltip') |
| 82 | const SeekbarElementInput = document.querySelector('#seek') |
| 83 | const SeekbarProgressbarElement = document.querySelector('.seekbar .seekbar-current-progress') |
| 84 | |
| 85 | |
| 86 | //////////////////////////////// |
| 87 | // Video Seekbar and Tooltip // |
| 88 | //////////////////////////////// |
| 89 | // Set max attribute to the range input |
| 90 | VideoElement.addEventListener('loadedmetadata', () => { |
| 91 | const VideoMaxLength = Math.round(VideoElement.duration).toString() |
| 92 | SeekbarElementInput.setAttribute("max", VideoMaxLength) |
| 93 | }) |
| 94 | |
| 95 | // Update progress bar and live timestamp |
| 96 | function UpdateSeekbar() { |
| 97 | SeekbarElementInput.value = Math.floor(VideoElement.currentTime) |
| 98 | SeekbarProgressbarElement.style.width = VideoElement.currentTime / VideoElement.duration * 100 + '%' |
| 99 | SeekbarTooltip.style.left = VideoElement.currentTime / VideoElement.duration * 100 + '%' |
| 100 | } |
| 101 | |
| 102 | // Format timestamp for Seekbar Tooltip |
| 103 | function TooltipFormat(Second) { |
| 104 | const result = new Date(Second * 1e3).toISOString().slice(11, 19) |
| 105 | return { |
| 106 | hours: result.slice(0, 2), |
| 107 | minutes: result.slice(3, 5), |
| 108 | seconds: result.slice(6, 8), |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | // Tooltip |
| 113 | function updateSeekTooltip(event) { |
| 114 | const skipTo = Math.round( |
| 115 | (event.offsetX / event.target.clientWidth) * |
| 116 | parseInt(event.target.getAttribute("max"), 10), |
| 117 | ) |
| 118 | SeekbarElementInput.setAttribute("data-seek", skipTo.toString()) |
| 119 | |
| 120 | const t = TooltipFormat(skipTo) |
| 121 | if (t.hours === "00") {SeekbarTooltip.textContent = `${t.minutes}:${t.seconds}`} |
| 122 | else {SeekbarTooltip.textContent = `${t.hours}:${t.minutes}:${t.seconds}`} |
| 123 | |
| 124 | const rect = SeekbarProgressbarElement.getBoundingClientRect() |
| 125 | SeekbarTooltip.style.left = `${event.pageX - rect.left}px` |
| 126 | SeekbarTooltip.style.fontSize = '12' |
| 127 | SeekbarElementInput.addEventListener('mouseleave', () => { |
| 128 | SeekbarTooltip.style.fontSize = '0' |
| 129 | SeekbarTooltip.style.left = VideoElement.currentTime / VideoElement.duration * 100 + '%' |
| 130 | }) |
| 131 | } |
| 132 | |
| 133 | // If end-users clicks anywhere on the seekbar, skip video current time to the point |
| 134 | SeekbarElementInput.addEventListener("mousemove", updateSeekTooltip) |
| 135 | function skipAhead(event) { |
| 136 | const skipTo = event.target.dataset.seek ? event.target.dataset.seek : event.target.value |
| 137 | VideoElement.currentTime = skipTo |
| 138 | SeekbarProgressbarElement.value = skipTo |
| 139 | SeekbarElementInput.value = skipTo |
| 140 | } |
| 141 | SeekbarElementInput.addEventListener("input", skipAhead) |
| 142 | |
| 143 | /////////////////////////////////// |
| 144 | // Video Duration and Timestamp // |
| 145 | /////////////////////////////////// |
| 146 | // Convert the video duration to a readable format |
| 147 | Number.prototype.VideoReadableFormat = function () { |
| 148 | let seconds = Math.floor(this), hours = Math.floor(seconds / 3600) |
| 149 | seconds -= hours*3600 |
| 150 | let minutes = Math.floor(seconds / 60) |
| 151 | seconds -= minutes*60 |
| 152 | if (hours < 10) {hours = "0"+hours} |
| 153 | if (minutes < 10) {minutes = "0"+minutes} |
| 154 | if (seconds < 10) {seconds = "0"+seconds} |
| 155 | if (hours === '00') { |
| 156 | // Hide 'hour' if the video not over an hour long |
| 157 | return minutes+':'+seconds |
| 158 | } else { |
| 159 | return hours+':'+minutes+':'+seconds |
| 160 | } |
| 161 | } |
| 162 | // When the video is playing, automatically update the 'Current Time' text |
| 163 | VideoElement.addEventListener('timeupdate', Live) |
| 164 | VideoElement.addEventListener('timeupdate', UpdateSeekbar) |
| 165 | function Live() {document.getElementById('text-live').innerText = `${VideoElement.currentTime.VideoReadableFormat()}`} |
| 166 | // Post the video's full duration |
| 167 | VideoElement.onloadedmetadata = function() {document.getElementById('text-full').innerText = `${VideoElement.duration.VideoReadableFormat()}`} |
| 168 | </script> |