UNPKG

quad-tap

Version:

A pure JavaScript implementation of the Quad-Tap overlay interaction for videos with advanced video player API integration

623 lines (529 loc) 22.1 kB
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>QuadTap Video API Integration Test</title><style>body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; } .container { max-width: 1200px; margin: 0 auto; padding: 20px; background-color: white; border-radius: 10px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } h1 { color: #333; margin-top: 0; } .video-container { position: relative; width: 100%; max-width: 800px; margin: 20px auto; border: 1px solid #ddd; border-radius: 5px; overflow: hidden; } video { width: 100%; display: block; } .controls { margin: 20px 0; padding: 15px; background-color: #f9f9f9; border-radius: 5px; border: 1px solid #ddd; } button { padding: 8px 16px; margin-right: 10px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; } button:hover { background-color: #45a049; } .event-log { margin-top: 20px; padding: 15px; background-color: #f9f9f9; border-radius: 5px; border: 1px solid #ddd; max-height: 300px; overflow-y: auto; } .event-log h3 { margin-top: 0; } .log-entry { margin-bottom: 5px; padding: 5px; border-bottom: 1px solid #eee; font-family: monospace; } .log-entry:last-child { border-bottom: none; } .log-time { color: #999; font-size: 0.8em; } .log-type { font-weight: bold; margin-right: 5px; } .log-type.info { color: #2196F3; } .log-type.error { color: #f44336; } .log-type.warn { color: #ff9800; } .log-type.success { color: #4CAF50; } .storage-viewer { margin-top: 20px; padding: 15px; background-color: #f9f9f9; border-radius: 5px; border: 1px solid #ddd; } .storage-viewer h3 { margin-top: 0; } .storage-item { margin-bottom: 5px; padding: 5px; border-bottom: 1px solid #eee; font-family: monospace; } .storage-key { font-weight: bold; color: #2196F3; } .storage-value { color: #333; } .coordinates-display { position: fixed; bottom: 10px; right: 10px; background-color: rgba(0, 0, 0, 0.7); color: white; padding: 5px 10px; border-radius: 5px; font-family: monospace; font-size: 12px; z-index: 9999; } .video-state { margin-top: 20px; padding: 15px; background-color: #f9f9f9; border-radius: 5px; border: 1px solid #ddd; } .video-state h3 { margin-top: 0; } .state-item { margin-bottom: 5px; padding: 5px; border-bottom: 1px solid #eee; font-family: monospace; } .state-key { font-weight: bold; color: #2196F3; display: inline-block; width: 150px; } .state-value { color: #333; } .state-value.playing { color: #4CAF50; font-weight: bold; } .state-value.paused { color: #f44336; font-weight: bold; } .coordinate-grid { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1000; opacity: 0.5; display: none; } .coordinate-grid.active { display: block; } .grid-line-h { position: absolute; left: 0; width: 100%; height: 1px; background-color: rgba(255, 255, 255, 0.3); } .grid-line-v { position: absolute; top: 0; width: 1px; height: 100%; background-color: rgba(255, 255, 255, 0.3); } .grid-center { position: absolute; top: 50%; left: 50%; width: 10px; height: 10px; background-color: rgba(255, 0, 0, 0.5); border-radius: 50%; transform: translate(-50%, -50%); } .quad-tap-controls { position: absolute; bottom: 10%; left: 50%; transform: translateX(-50%); z-index: 1000; background-color: rgba(0, 0, 0, 0.7); padding: 10px; border-radius: 5px; display: flex; justify-content: center; align-items: center; } .quad-tap-control-button { background-color: transparent; border: none; color: white; font-size: 24px; margin: 0 10px; cursor: pointer; padding: 5px; border-radius: 50%; width: 40px; height: 40px; display: flex; justify-content: center; align-items: center; } .quad-tap-control-button:hover { background-color: rgba(255, 255, 255, 0.2); } .quad-tap-emoji { position: absolute; font-size: 24px; z-index: 1000; } .quad-tap-emoji-ne { top: 5%; right: 5%; } .quad-tap-emoji-nw { top: 5%; left: 5%; } .quad-tap-emoji-se { bottom: 5%; right: 5%; } .quad-tap-emoji-sw { bottom: 5%; left: 5%; }</style><script src="quad-tap.js"></script></head><body><div class="container"><h1>QuadTap Video API Integration Test</h1><p>This page demonstrates the integration of QuadTap with video player APIs, ensuring the video only pauses when the lightbox is open.</p><div class="video-container" id="main-video-container"><video id="main-video" controls data-video-id="test-video-123"><source src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" type="video/mp4">Your browser does not support the video tag.</video><div class="coordinate-grid" id="coordinate-grid"><div class="grid-line-h" style="top: 25%"></div><div class="grid-line-h" style="top: 50%"></div><div class="grid-line-h" style="top: 75%"></div><div class="grid-line-v" style="left: 25%"></div><div class="grid-line-v" style="left: 50%"></div><div class="grid-line-v" style="left: 75%"></div><div class="grid-center"></div></div></div><div class="controls"><button id="init-btn">Initialize QuadTap</button> <button id="destroy-btn">Destroy QuadTap</button> <button id="clear-log-btn">Clear Log</button> <button id="clear-storage-btn">Clear Storage</button> <button id="toggle-grid-btn">Toggle Grid</button></div><div class="video-state"><h3>Video State</h3><div id="video-state-container"><div class="state-item"><span class="state-key">Playing:</span> <span class="state-value" id="video-playing">false</span></div><div class="state-item"><span class="state-key">Current Time:</span> <span class="state-value" id="video-current-time">0</span></div><div class="state-item"><span class="state-key">Duration:</span> <span class="state-value" id="video-duration">0</span></div><div class="state-item"><span class="state-key">Volume:</span> <span class="state-value" id="video-volume">0</span></div><div class="state-item"><span class="state-key">Muted:</span> <span class="state-value" id="video-muted">false</span></div><div class="state-item"><span class="state-key">Playback Rate:</span> <span class="state-value" id="video-playback-rate">1</span></div></div></div><div class="event-log"><h3>Event Log</h3><div id="log-container"></div></div><div class="storage-viewer"><h3>Local Storage</h3><div id="storage-container"></div></div></div><div class="coordinates-display" id="coordinates-display">X: 0, Y: 0</div><script src="../dist/quad-tap.js"></script><script>document.addEventListener('DOMContentLoaded', function() { // Initialize elements const initBtn = document.getElementById('init-btn'); const destroyBtn = document.getElementById('destroy-btn'); const clearLogBtn = document.getElementById('clear-log-btn'); const clearStorageBtn = document.getElementById('clear-storage-btn'); const toggleGridBtn = document.getElementById('toggle-grid-btn'); const logContainer = document.getElementById('log-container'); const storageContainer = document.getElementById('storage-container'); const coordinatesDisplay = document.getElementById('coordinates-display'); const videoElement = document.getElementById('main-video'); const videoContainer = document.getElementById('main-video-container'); const coordinateGrid = document.getElementById('coordinate-grid'); // Video state elements const videoPlayingElement = document.getElementById('video-playing'); const videoCurrentTimeElement = document.getElementById('video-current-time'); const videoDurationElement = document.getElementById('video-duration'); const videoVolumeElement = document.getElementById('video-volume'); const videoMutedElement = document.getElementById('video-muted'); const videoPlaybackRateElement = document.getElementById('video-playback-rate'); // Toggle grid toggleGridBtn.addEventListener('click', function() { coordinateGrid.classList.toggle('active'); }); // Track mouse coordinates videoContainer.addEventListener('mousemove', function(evt) { const rect = videoContainer.getBoundingClientRect(); const x = evt.clientX - rect.left; const y = evt.clientY - rect.top; // Get container dimensions const containerWidth = rect.width; const containerHeight = rect.height; // Calculate normalized coordinates let normalizedX = 0; let normalizedY = 0; let percentX = 0; let percentY = 0; // Check if QuadTap and Coordinates are available if (typeof QuadTap !== 'undefined' && typeof QuadTap.Coordinates !== 'undefined') { const normalized = QuadTap.Coordinates.normalize(x, y, containerWidth, containerHeight); normalizedX = normalized.normalizedX; normalizedY = normalized.normalizedY; const percentage = QuadTap.Coordinates.toPercentage(x, y, containerWidth, containerHeight); percentX = percentage.percentX; percentY = percentage.percentY; } else { // Fallback if Coordinates utility is not available normalizedX = containerWidth > 0 ? x / containerWidth : 0; normalizedY = containerHeight > 0 ? y / containerHeight : 0; percentX = normalizedX * 100; percentY = normalizedY * 100; } // Update coordinates display coordinatesDisplay.textContent = `X: ${Math.round(x)}, Y: ${Math.round(y)} | ` + `Norm: ${normalizedX.toFixed(2)}, ${normalizedY.toFixed(2)} | ` + `%: ${percentX.toFixed(0)}%, ${percentY.toFixed(0)}%`; }); // Override console.log to capture QuadTap logs const originalConsoleLog = console.log; console.log = function() { // Call original console.log originalConsoleLog.apply(console, arguments); // Check if this is a QuadTap log if (arguments.length > 0 && typeof arguments[0] === 'string' && (arguments[0].includes('[QuadTap]') || arguments[0].includes('[VideoPlayerAdapter]') || arguments[0].includes('[Coordinates]'))) { // Add to log container addLogEntry('info', arguments[0], arguments[1] || ''); } }; // Add log entry function addLogEntry(type, message, data) { const logEntry = document.createElement('div'); logEntry.className = 'log-entry'; const time = new Date().toLocaleTimeString(); const logTime = document.createElement('span'); logTime.className = 'log-time'; logTime.textContent = `[${time}] `; const logType = document.createElement('span'); logType.className = `log-type ${type}`; logType.textContent = type.toUpperCase(); const logMessage = document.createElement('span'); logMessage.className = 'log-message'; logMessage.textContent = ` ${message}`; logEntry.appendChild(logTime); logEntry.appendChild(logType); logEntry.appendChild(logMessage); if (data) { const logData = document.createElement('div'); logData.className = 'log-data'; logData.textContent = typeof data === 'object' ? JSON.stringify(data, null, 2) : data; logEntry.appendChild(logData); } logContainer.appendChild(logEntry); logContainer.scrollTop = logContainer.scrollHeight; } // Update storage viewer function updateStorageViewer() { storageContainer.innerHTML = ''; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); // Only show QuadTap storage items if (key.startsWith('quadTap_')) { const value = localStorage.getItem(key); const storageItem = document.createElement('div'); storageItem.className = 'storage-item'; const storageKey = document.createElement('span'); storageKey.className = 'storage-key'; storageKey.textContent = key; const storageValue = document.createElement('span'); storageValue.className = 'storage-value'; storageValue.textContent = `: ${value}`; storageItem.appendChild(storageKey); storageItem.appendChild(storageValue); storageContainer.appendChild(storageItem); } } } // Update video state display function updateVideoStateDisplay() { const isPlaying = !videoElement.paused; videoPlayingElement.textContent = isPlaying ? 'true' : 'false'; videoPlayingElement.className = `state-value ${isPlaying ? 'playing' : 'paused'}`; videoCurrentTimeElement.textContent = videoElement.currentTime.toFixed(2); videoDurationElement.textContent = videoElement.duration.toFixed(2); videoVolumeElement.textContent = videoElement.volume.toFixed(2); videoMutedElement.textContent = videoElement.muted ? 'true' : 'false'; videoPlaybackRateElement.textContent = videoElement.playbackRate.toFixed(2); } // Add video event listeners videoElement.addEventListener('play', updateVideoStateDisplay); videoElement.addEventListener('pause', updateVideoStateDisplay); videoElement.addEventListener('timeupdate', updateVideoStateDisplay); videoElement.addEventListener('volumechange', updateVideoStateDisplay); videoElement.addEventListener('ratechange', updateVideoStateDisplay); // Initialize video state display videoElement.addEventListener('loadedmetadata', updateVideoStateDisplay); // Initialize QuadTap let quadTap = null; initBtn.addEventListener('click', function() { if (!quadTap) { addLogEntry('info', 'Initializing QuadTap with VideoPlayerAdapter...'); // Check if QuadTap is available if (typeof QuadTap === 'undefined') { addLogEntry('error', 'QuadTap is not defined. Make sure the bundle is loaded.'); return; } try { // Check if VideoPlayerAdapter is available if (typeof QuadTap.VideoPlayerAdapter === 'undefined') { addLogEntry('error', 'VideoPlayerAdapter is not defined. Make sure the bundle is loaded correctly.'); return; } // Create a video player adapter const videoAdapter = QuadTap.VideoPlayerAdapter.forHtml5Video(videoElement, true); addLogEntry('success', 'Created VideoPlayerAdapter for HTML5 video'); // Check if SettingsBuilder is available if (typeof QuadTap.SettingsBuilder === 'undefined') { addLogEntry('error', 'SettingsBuilder is not defined. Make sure the bundle is loaded correctly.'); return; } // Build settings with fluent interface const settingsBuilder = new QuadTap.SettingsBuilder() .withContainer('main-video-container') .withVideoSelector('#main-video') .withDebug(true) .withAutoCancelTimeout(5000) .withVideoPlayerApi({ enabled: true, playMethod: () => videoAdapter.play(), pauseMethod: () => videoAdapter.pause(), seekMethod: (time) => videoAdapter.seek(time), getCurrentTimeMethod: () => videoAdapter.getCurrentTime(), getDurationMethod: () => videoAdapter.getDuration(), getVideoIdMethod: () => videoAdapter.getVideoId() }) .withCoordinateSystem({ type: 'normalized', // 'normalized', 'percentage', or 'absolute' storeMetadata: true // Include container dimensions in stored data }) .withEmojiSizes({ default: '24px', active: '36px' }) .onOverlayActivate((coordinates) => { addLogEntry('success', 'Overlay activated', coordinates); // Video should continue playing when overlay is activated addLogEntry('info', 'Video should continue playing when overlay is activated'); }) .onThrowDownInitiate((quadrant, coordinates) => { addLogEntry('success', 'Throw-down initiated', { quadrant, coordinates }); }) .onThrowDownConfirm((quadrant, coordinates, videoInfo) => { addLogEntry('success', 'Throw-down confirmed', { quadrant, coordinates, videoInfo }); updateStorageViewer(); }) .onThrowDownCancel((quadrant) => { addLogEntry('warn', 'Throw-down canceled', { quadrant }); }) .onVideoControl((action, currentTime) => { addLogEntry('info', 'Video control action', { action, currentTime }); }); // Build the settings const settings = settingsBuilder.build(); addLogEntry('info', 'Built settings with SettingsBuilder'); // Initialize QuadTap with the settings quadTap = new QuadTap(settings); addLogEntry('success', 'QuadTap initialized with VideoPlayerAdapter'); } catch (error) { addLogEntry('error', 'Error initializing QuadTap: ' + error.message); console.error(error); } } else { addLogEntry('warn', 'QuadTap is already initialized'); } }); // Destroy QuadTap destroyBtn.addEventListener('click', function() { if (quadTap) { quadTap.destroy(); quadTap = null; addLogEntry('info', 'QuadTap destroyed'); } else { addLogEntry('warn', 'QuadTap is not initialized'); } }); // Clear log clearLogBtn.addEventListener('click', function() { logContainer.innerHTML = ''; addLogEntry('info', 'Log cleared'); }); // Clear storage clearStorageBtn.addEventListener('click', function() { // Clear only QuadTap storage items const keysToRemove = []; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key.startsWith('quadTap_')) { keysToRemove.push(key); } } keysToRemove.forEach(key => { localStorage.removeItem(key); }); updateStorageViewer(); addLogEntry('info', 'Storage cleared'); }); // Initialize storage viewer updateStorageViewer(); // Add initial log entry addLogEntry('info', 'Video API test page loaded'); addLogEntry('info', 'This test demonstrates the integration with video player APIs'); addLogEntry('info', 'The video should ONLY pause when the lightbox is open, not when the overlay appears'); // Check if QuadTap is available if (typeof QuadTap === 'undefined') { addLogEntry('error', 'QuadTap is not defined. Make sure the bundle is loaded.'); } else { addLogEntry('success', 'QuadTap is available'); // Check if VideoPlayerAdapter is available if (typeof QuadTap.VideoPlayerAdapter === 'undefined') { addLogEntry('error', 'VideoPlayerAdapter is not defined. Make sure the bundle is loaded correctly.'); } else { addLogEntry('success', 'VideoPlayerAdapter is available'); } // Check if SettingsBuilder is available if (typeof QuadTap.SettingsBuilder === 'undefined') { addLogEntry('error', 'SettingsBuilder is not defined. Make sure the bundle is loaded correctly.'); } else { addLogEntry('success', 'SettingsBuilder is available'); } // Check if Coordinates is available if (typeof QuadTap.Coordinates === 'undefined') { addLogEntry('error', 'Coordinates is not defined. Make sure the bundle is loaded correctly.'); } else { addLogEntry('success', 'Coordinates is available'); } } });</script></body></html>