JSFiddle - React, Tailwind, and code Playground

by Milan Gladiš

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Video Player with Timeline</title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@800&display=swap">
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="clickCounterWrapper">
    <svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
      <g filter="url(#filter0_i_6058_13290)">
      <rect x="1.5" y="2" width="21" height="21" rx="10.5" fill="url(#paint0_linear_6058_13290)"/>
      </g>
      <rect x="3" y="3.5" width="18" height="18" rx="9" stroke="url(#paint1_linear_6058_13290)" stroke-width="3"/>
      <defs>
      <filter id="filter0_i_6058_13290" x="1.5" y="2" width="21" height="22" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
      <feFlood flood-opacity="0" result="BackgroundImageFix"/>
      <feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
      <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
      <feOffset dy="1"/>
      <feGaussianBlur stdDeviation="4"/>
      <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
      <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
      <feBlend mode="normal" in2="shape" result="effect1_innerShadow_6058_13290"/>
      </filter>
      <linearGradient id="paint0_linear_6058_13290" x1="1.5" y1="2" x2="22.5" y2="23" gradientUnits="userSpaceOnUse">
      <stop stop-color="#FFBD01"/>
      <stop offset="1" stop-color="#FBA300"/>
      </linearGradient>
      <linearGradient id="paint1_linear_6058_13290" x1="12" y1="2" x2="12" y2="23" gradientUnits="userSpaceOnUse">
      <stop stop-color="#FFDE8C"/>
      <stop offset="1" stop-color="#FDC95E"/>
      </linearGradient>
      </defs>
      </svg>

    ...

CSS

body, html {
  padding: 0;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  font-family: 'Inter', sans-serif;
}

video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

button {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}

video::-webkit-media-controls-panel {
  display: none !important;
  opacity: 1 !important;
}

#intro {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%);
  z-index: 10;
  font-size: 24px;
  color: white;
  pointer-events: none;
}

#intro.hide {
  transform: translateY(-30px);
  opacity: 0;
}

.point {
  position: absolute;
  font-size: 32px;
  color: white;
  font-weight: 800;
  z-index: 100;
  transform: translate(-50%, -100%);
  animation: moveUpFadeOut 0.3s forwards;
}

@keyframes moveUpFadeOut {
  0% {
    transform: translate(-50%, -50px);
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -80px);
    opacity: 0;
  }
}

.clickCounterWrapper {
  display: flex;
  align-items: center;
  gap: 8px;
  position: absolute;
  top: 24px;
  right: 24px;
  font-size: 24px;
  color: white;
  font-weight: 800;
  background-color: rgba(0, 0, 0, 0.5);
  padding: 8px 16px;
  border-radius: 8px;
}

.clickCounterWrapper span {
  min-width: -32px;
  display: block;
}

#booster {
  position: absolute;
  bottom: 20px;
  left: 20px;
  font-size: 20px;
  font-weight: 800; /* Extra bold */
  background-color: rgba(0, 0, 0, 0.5);
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 10px;
  cursor: pointer;
}

JavaScript

document.addEventListener('DOMContentLoaded', function() {
    const video = document.getElementById('myVideo');
    const playByPortionButton = document.getElementById('playByPortion');
    const clickCounter = document.getElementById('clickCounter');
	  const intro = document.getElementById('intro');
    let clickCount = 0;

    playByPortionButton.addEventListener('click', function(event) {

				intro.classList.add('hide');
        const portion = video.duration / 1000;
        video.currentTime += portion;

        // Increment the click counter
        clickCount++;
        clickCounter.textContent = clickCount;

        // Get the click position
        const x = event.clientX;
        const y = event.clientY;

        // Create the +1 element
        const point = document.createElement('div');
        point.classList.add('point');
        point.textContent = '+1';
        point.style.left = `${x}px`;
        point.style.top = `${y}px`;
        document.body.appendChild(point);

        // Remove the element after animation completes
        point.addEventListener('animationend', function() {
            point.remove();
        });
    });

    video.addEventListener('loadedmetadata', function() {
        // This ensures the video duration is available before clicking the button
        console.log('Video duration:', video.duration);
    });
});