|
230 | 230 | cursor: pointer; |
231 | 231 | } |
232 | 232 |
|
233 | | - /* Video play overlay container */ |
234 | | - .video-container { |
| 233 | + /* |
| 234 | + * ===================================================================== |
| 235 | + * Generic Video Play Overlay Styles |
| 236 | + * ===================================================================== |
| 237 | + * Usage: Add the attribute `data-play-overlay` to any <video> element. |
| 238 | + * The JavaScript below will automatically wrap the video in a container |
| 239 | + * and inject a clickable/keyboard-accessible play overlay. |
| 240 | + * |
| 241 | + * Example: |
| 242 | + * <video data-play-overlay poster="thumb.png" ...> |
| 243 | + * <source src="video.mp4" type="video/mp4"> |
| 244 | + * </video> |
| 245 | + * ===================================================================== |
| 246 | + */ |
| 247 | + |
| 248 | + /* Container injected around each <video data-play-overlay> */ |
| 249 | + .video-overlay-container { |
235 | 250 | position: relative; |
236 | 251 | display: inline-block; |
237 | 252 | width: 100%; |
238 | 253 | } |
239 | 254 |
|
240 | | - /* Video play overlay button */ |
| 255 | + /* Overlay button covering the video; hidden when .hidden class is added */ |
241 | 256 | .video-play-overlay { |
242 | 257 | position: absolute; |
243 | 258 | top: 0; |
|
263 | 278 | outline-offset: 2px; |
264 | 279 | } |
265 | 280 |
|
266 | | - /* Play icon: circle with triangle */ |
| 281 | + /* Play icon: white circle with CSS triangle */ |
267 | 282 | .play-icon { |
268 | 283 | width: 72px; |
269 | 284 | height: 72px; |
@@ -487,16 +502,11 @@ <h2>Screenshots</h2> |
487 | 502 | <img src="screenshots/light/0.0.6/mpos_camera_qr_320x240.png" alt="Camera QR Code Screenshot"/> |
488 | 503 | <img src="screenshots/light/0.0.6/osupdate_progress3.png" alt="OS Update Progress Screenshot"/> |
489 | 504 | <img src="screenshots/dark/0.0.4/0.0.4_launcher1.png" alt="Dark Theme Launcher Screenshot"/> |
490 | | - <!-- Video play overlay: wraps video with clickable play button --> |
491 | | - <div class="video-container"> |
492 | | - <video id="quasibird-video" playsinline poster="screenshots/light/0.3.2/com.quasikili.quasibird_0.0.3.png" width="320" height="240" preload="auto"> |
493 | | - <source src="videos/QuasiBird_0.0.3.mp4" type="video/mp4"> |
494 | | - <img src="screenshots/light/0.3.2/com.quasikili.quasibird_0.0.3.png" alt="QuasiBird game in action"> |
495 | | - </video> |
496 | | - <div class="video-play-overlay" id="quasibird-play-overlay" tabindex="0" role="button" aria-label="Play video"> |
497 | | - <div class="play-icon"></div> |
498 | | - </div> |
499 | | - </div> |
| 505 | + <!-- To add a play overlay to any video, just add the data-play-overlay attribute --> |
| 506 | + <video data-play-overlay loop muted playsinline poster="screenshots/light/0.3.2/com.quasikili.quasibird_0.0.3.png" width="320" height="240" preload="auto"> |
| 507 | + <source src="videos/QuasiBird_0.0.3.mp4" type="video/mp4"> |
| 508 | + <img src="screenshots/light/0.3.2/com.quasikili.quasibird_0.0.3.png" alt="QuasiBird game in action"> |
| 509 | + </video> |
500 | 510 | <img src="screenshots/dark/0.3.3/wifi_password.png" alt="Dark Theme WiFi Password Screenshot"/> |
501 | 511 | <img src="screenshots/dark/0.0.4/0.0.4_appstore_uninstall_draw.png" alt="Dark Theme App Store Uninstall Screenshot"/> |
502 | 512 | <img src="screenshots/dark/0.0.4/0.0.4_imu1.png" alt="Dark Theme IMU Screenshot"/> |
@@ -573,29 +583,83 @@ <h2>Possibilities</h2> |
573 | 583 | } |
574 | 584 | </script> |
575 | 585 |
|
576 | | - <!-- JavaScript for Video Play Overlay --> |
| 586 | + <!-- |
| 587 | + ========================================================================= |
| 588 | + Generic Video Play Overlay Script |
| 589 | + ========================================================================= |
| 590 | + Automatically initializes play overlays for all <video data-play-overlay> |
| 591 | + elements. The script: |
| 592 | + 1. Wraps each video in a .video-overlay-container |
| 593 | + 2. Injects a .video-play-overlay button with a CSS play icon |
| 594 | + 3. Handles click, Enter, and Space to play the video |
| 595 | + 4. Shows native controls after playback starts |
| 596 | + 5. Re-shows the overlay when the video ends (for replay) |
| 597 | +
|
| 598 | + To opt-in a new video, simply add the data-play-overlay attribute: |
| 599 | + <video data-play-overlay poster="thumb.png" ...> |
| 600 | + <source src="video.mp4" type="video/mp4"> |
| 601 | + </video> |
| 602 | + ========================================================================= |
| 603 | + --> |
577 | 604 | <script> |
578 | | - (function() { |
579 | | - const overlay = document.getElementById('quasibird-play-overlay'); |
580 | | - const video = document.getElementById('quasibird-video'); |
581 | | - |
582 | | - if (overlay && video) { |
583 | | - function playVideo() { |
| 605 | + (function initVideoPlayOverlays() { |
| 606 | + // Prevent double-initialization |
| 607 | + if (window.__videoPlayOverlaysInitialized) return; |
| 608 | + window.__videoPlayOverlaysInitialized = true; |
| 609 | + |
| 610 | + const videos = document.querySelectorAll('video[data-play-overlay]'); |
| 611 | + if (!videos.length) return; |
| 612 | + |
| 613 | + videos.forEach(function(video) { |
| 614 | + // Remove autoplay to ensure video doesn't start on page load |
| 615 | + video.removeAttribute('autoplay'); |
| 616 | + |
| 617 | + // Create container wrapper |
| 618 | + const container = document.createElement('div'); |
| 619 | + container.className = 'video-overlay-container'; |
| 620 | + |
| 621 | + // Insert container before video, then move video inside |
| 622 | + video.parentNode.insertBefore(container, video); |
| 623 | + container.appendChild(video); |
| 624 | + |
| 625 | + // Create overlay button |
| 626 | + const overlay = document.createElement('div'); |
| 627 | + overlay.className = 'video-play-overlay'; |
| 628 | + overlay.setAttribute('tabindex', '0'); |
| 629 | + overlay.setAttribute('role', 'button'); |
| 630 | + overlay.setAttribute('aria-label', 'Play video'); |
| 631 | + |
| 632 | + // Create play icon (CSS-styled circle + triangle) |
| 633 | + const playIcon = document.createElement('div'); |
| 634 | + playIcon.className = 'play-icon'; |
| 635 | + overlay.appendChild(playIcon); |
| 636 | + |
| 637 | + // Insert overlay into container |
| 638 | + container.appendChild(overlay); |
| 639 | + |
| 640 | + // Activation handler: play video, hide overlay, show controls |
| 641 | + function activateVideo() { |
584 | 642 | video.play(); |
585 | 643 | overlay.classList.add('hidden'); |
| 644 | + video.controls = true; |
586 | 645 | } |
587 | 646 |
|
588 | 647 | // Click handler |
589 | | - overlay.addEventListener('click', playVideo); |
| 648 | + overlay.addEventListener('click', activateVideo); |
590 | 649 |
|
591 | 650 | // Keyboard handler (Enter or Space) |
592 | 651 | overlay.addEventListener('keydown', function(e) { |
593 | 652 | if (e.key === 'Enter' || e.key === ' ') { |
594 | 653 | e.preventDefault(); |
595 | | - playVideo(); |
| 654 | + activateVideo(); |
596 | 655 | } |
597 | 656 | }); |
598 | | - } |
| 657 | + |
| 658 | + // When video ends, show overlay again for replay |
| 659 | + video.addEventListener('ended', function() { |
| 660 | + overlay.classList.remove('hidden'); |
| 661 | + }); |
| 662 | + }); |
599 | 663 | })(); |
600 | 664 | </script> |
601 | 665 | </body> |
|
0 commit comments