commit 6c86d4d202cf08fb02c0b2eb3bd4f9761d082f4a
Author: root <root@hub.scroll.pub> Date: 2024-12-27 23:15:14 +0000 Subject: Initial commit diff --git a/body.html b/body.html new file mode 100644 index 0000000..8ea2de7 --- /dev/null +++ b/body.html @@ -0,0 +1,29 @@ +<div class="video-container"> + <div class="video-wrapper"> + <video id="main-video" controls> + <source src="sample.mp4" type="video/mp4"> + Your browser does not support the video tag. + </video> + <div class="controls"> + <button class="control-btn" id="play-pause">▶️</button> + <button class="control-btn" id="fullscreen">⛶</button> + <div class="seek-bar"> + <input type="range" id="seek-slider" min="0" max="100" value="0"> + </div> + </div> + </div> +</div> + +<div class="orientation-notice"> + <p>Rotate your device for different viewing modes!</p> +</div> + +<div class="instructions"> + <h2>How to Use</h2> + <ul> + <li>📱 Portrait Mode: Zoom and pan with pinch gestures</li> + <li>🖥️ Landscape Mode: Full 16:9 view</li> + <li>👆 Double tap to skip 10 seconds</li> + <li>🔄 Swipe vertically to change videos</li> + </ul> +</div> \ No newline at end of file diff --git a/index.scroll b/index.scroll new file mode 100644 index 0000000..135c832 --- /dev/null +++ b/index.scroll @@ -0,0 +1,8 @@ +buildHtml +baseUrl https://immersiveplayer.scroll.pub +metaTags +editButton /edit.html +title Immersive Video Player +style.css +body.html +script.js \ No newline at end of file diff --git a/prompt.json b/prompt.json new file mode 100644 index 0000000..aa51f29 --- /dev/null +++ b/prompt.json @@ -0,0 +1,25 @@ +{ + "id": "ffee3901-ca54-4a73-9bdf-21699c370b0b", + "object": "chat.completion", + "created": 1735341292, + "model": "deepseek-chat", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "---domain---\nimmersiveplayer.scroll.pub\n---index.scroll---\nbuildHtml\nbaseUrl https://immersiveplayer.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Immersive Video Player\nstyle.css\nbody.html\nscript.js\n---body.html---\n<div class=\"video-container\">\n <div class=\"video-wrapper\">\n <video id=\"main-video\" controls>\n <source src=\"sample.mp4\" type=\"video/mp4\">\n Your browser does not support the video tag.\n </video>\n <div class=\"controls\">\n <button class=\"control-btn\" id=\"play-pause\">▶️</button>\n <button class=\"control-btn\" id=\"fullscreen\">⛶</button>\n <div class=\"seek-bar\">\n <input type=\"range\" id=\"seek-slider\" min=\"0\" max=\"100\" value=\"0\">\n </div>\n </div>\n </div>\n</div>\n\n<div class=\"orientation-notice\">\n <p>Rotate your device for different viewing modes!</p>\n</div>\n\n<div class=\"instructions\">\n <h2>How to Use</h2>\n <ul>\n <li>📱 Portrait Mode: Zoom and pan with pinch gestures</li>\n <li>🖥️ Landscape Mode: Full 16:9 view</li>\n <li>👆 Double tap to skip 10 seconds</li>\n <li>🔄 Swipe vertically to change videos</li>\n </ul>\n</div>\n---style.css---\n:root {\n --primary-color: #1e90ff;\n --control-bg: rgba(0, 0, 0, 0.7);\n --control-size: 40px;\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Segoe UI', sans-serif;\n background: #000;\n color: #fff;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n}\n\n.video-container {\n width: 100%;\n max-width: 800px;\n position: relative;\n overflow: hidden;\n border-radius: 15px;\n box-shadow: 0 0 20px rgba(255, 255, 255, 0.1);\n}\n\n.video-wrapper {\n position: relative;\n padding-top: 56.25%;\n}\n\nvideo {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n background: var(--control-bg);\n padding: 10px;\n display: flex;\n align-items: center;\n gap: 10px;\n opacity: 0;\n transition: opacity 0.3s ease;\n}\n\n.video-container:hover .controls {\n opacity: 1;\n}\n\n.control-btn {\n background: none;\n border: none;\n color: #fff;\n font-size: 1.2rem;\n cursor: pointer;\n padding: 5px;\n transition: transform 0.2s ease;\n}\n\n.control-btn:hover {\n transform: scale(1.1);\n}\n\n.seek-bar {\n flex: 1;\n}\n\n#seek-slider {\n width: 100%;\n cursor: pointer;\n}\n\n.orientation-notice {\n margin-top: 20px;\n text-align: center;\n font-size: 1.1rem;\n color: #aaa;\n}\n\n.instructions {\n margin-top: 30px;\n padding: 20px;\n background: rgba(255, 255, 255, 0.05);\n border-radius: 10px;\n max-width: 800px;\n}\n\n.instructions h2 {\n margin-bottom: 15px;\n color: var(--primary-color);\n}\n\n.instructions ul {\n list-style: none;\n padding: 0;\n}\n\n.instructions li {\n margin: 10px 0;\n font-size: 1rem;\n color: #ddd;\n}\n\n@media (max-width: 768px) {\n .video-container {\n border-radius: 0;\n }\n \n .instructions {\n margin: 20px;\n }\n}\n---script.js---\nconst video = document.getElementById('main-video');\nconst playPauseBtn = document.getElementById('play-pause');\nconst fullscreenBtn = document.getElementById('fullscreen');\nconst seekSlider = document.getElementById('seek-slider');\n\nlet isZoomed = false;\nlet startX, startY, translateX = 0, translateY = 0;\n\n// Play/Pause functionality\nplayPauseBtn.addEventListener('click', () => {\n if (video.paused) {\n video.play();\n playPauseBtn.textContent = '⏸️';\n } else {\n video.pause();\n playPauseBtn.textContent = '▶️';\n }\n});\n\n// Fullscreen functionality\nfullscreenBtn.addEventListener('click', () => {\n if (!document.fullscreenElement) {\n video.requestFullscreen();\n } else {\n document.exitFullscreen();\n }\n});\n\n// Seek functionality\nseekSlider.addEventListener('input', () => {\n const seekTime = video.duration * (seekSlider.value / 100);\n video.currentTime = seekTime;\n});\n\nvideo.addEventListener('timeupdate', () => {\n const value = (100 / video.duration) * video.currentTime;\n seekSlider.value = value;\n});\n\n// Double tap to skip\nlet lastTap = 0;\nvideo.addEventListener('touchend', (e) => {\n const currentTime = new Date().getTime();\n const tapLength = currentTime - lastTap;\n \n if (tapLength < 300 && tapLength > 0) {\n const skipTime = e.touches[0].clientX < window.innerWidth / 2 ? -10 : 10;\n video.currentTime += skipTime;\n }\n \n lastTap = currentTime;\n});\n\n// Handle orientation changes\nwindow.addEventListener('orientationchange', () => {\n if (isZoomed) {\n resetZoom();\n }\n});\n\n// Zoom and pan functionality\nvideo.addEventListener('touchstart', (e) => {\n if (e.touches.length === 2) {\n isZoomed = true;\n startX = (e.touches[0].pageX + e.touches[1].pageX) / 2;\n startY = (e.touches[0].pageY + e.touches[1].pageY) / 2;\n }\n});\n\nvideo.addEventListener('touchmove', (e) => {\n if (isZoomed && e.touches.length === 2) {\n const scale = Math.hypot(\n e.touches[0].pageX - e.touches[1].pageX,\n e.touches[0].pageY - e.touches[1].pageY\n ) / Math.hypot(\n e.touches[0].pageX - e.touches[1].pageX,\n e.touches[0].pageY - e.touches[1].pageY\n );\n\n const currentX = (e.touches[0].pageX + e.touches[1].pageX) / 2;\n const currentY = (e.touches[0].pageY + e.touches[1].pageY) / 2;\n\n translateX += currentX - startX;\n translateY += currentY - startY;\n\n video.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;\n video.style.transformOrigin = 'center center';\n }\n});\n\nvideo.addEventListener('touchend', () => {\n if (isZoomed) {\n isZoomed = false;\n }\n});\n\nfunction resetZoom() {\n video.style.transform = 'none';\n translateX = 0;\n translateY = 0;\n}\n---end---" + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 1113, + "completion_tokens": 1767, + "total_tokens": 2880, + "prompt_cache_hit_tokens": 0, + "prompt_cache_miss_tokens": 1113 + }, + "system_fingerprint": "fp_f1afce2943" +} \ No newline at end of file diff --git a/readme.scroll b/readme.scroll new file mode 100644 index 0000000..adbb242 --- /dev/null +++ b/readme.scroll @@ -0,0 +1,2 @@ +# immersiveplayer.scroll.pub +Website generated by DeepSeek from prompt: Descrizione del videoplayer: Modalità Portrait (Verticale): • Visualizzazione principale: • Quando un filmato è in formato 16:9, viene zoomato per riempire l’intero schermo in modalità portrait. Questo significa che il video mostra la parte centrale (sia orizzontale che verticale) del contenuto, sacrificando le porzioni laterali del video. • Interazione Zoom: • È possibile zoomare in ulteriormente tramite un gesto di pinch-to-zoom (allargando le dita), mantenendo però il video a schermo intero. Lo zoom non può essere ridotto al punto di lasciare bordi neri o spazi vuoti. • Una volta zoomato, l’utente può muoversi all’interno del video trascinando con il dito per spostare la porzione visibile. • Navigazione video: • L’interfaccia consente di scorrere i video in stile TikTok con uno swipe verso l’alto o il basso per passare al video successivo o precedente. Modalità Landscape (Orizzontale): • Visualizzazione principale: • Quando lo schermo viene girato in modalità landscape, il video si ridimensiona automaticamente per adattarsi al formato 16:9 e riempire l’intero schermo orizzontale. • Non ci sono ritagli o porzioni nascoste in questa modalità: il video viene visualizzato nella sua interezza. • Interazione Zoom: • Anche in modalità landscape, l’utente può effettuare il pinch-to-zoom per ingrandire il video. Come nella modalità portrait, il video rimane fullscreen e le aree ingrandite possono essere esplorate trascinando il dito. • Non è possibile zoomare out a un punto in cui ci siano bordi neri. Comportamenti comuni: 1. Transizione Portrait-Landscape: • Il passaggio tra portrait e landscape è fluido: in modalità portrait, il video torna automaticamente al livello di zoom iniziale (se c’era uno zoom) e centra il contenuto visibile. • In modalità landscape, il video viene adattato al formato 16:9 senza interruzioni o lag. 2. Gestione dei controlli: • Controlli minimalisti per play, pausa, avanti/indietro, volume e modalità schermo intero. • Possibilità di doppio tap per avanzare o riavvolgere di 10 secondi. 3. Funzione Drag-to-Pan: • In entrambe le modalità, quando il video è zoomato, si può trascinare con il dito per esplorare diverse parti della scena. Il movimento è fluido e limitato ai bordi del video, evitando aree nere. 4. Compatibilità video: • Supporta video con aspect ratio diversi (1:1, 9:16, ecc.) adattando lo zoom automaticamente per riempire lo schermo nella modalità corrente. UI e Animazioni: • UI minimalista: • I controlli sono semi-trasparenti e scompaiono automaticamente dopo pochi secondi di inattività, riapparendo con un tap sullo schermo. • Animazioni fluide: • Il passaggio tra zoom e pan è accompagnato da transizioni fluide per una sensazione di naturalezza durante l’interazione. Questo videoplayer è progettato per massimizzare l’esperienza immersiva indipendentemente dall’orientamento del dispositivo, offrendo una combinazione tra flessibilità di zoom e una visualizzazione sempre a schermo intero. \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..21fe920 --- /dev/null +++ b/script.js @@ -0,0 +1,101 @@ +const video = document.getElementById('main-video'); +const playPauseBtn = document.getElementById('play-pause'); +const fullscreenBtn = document.getElementById('fullscreen'); +const seekSlider = document.getElementById('seek-slider'); + +let isZoomed = false; +let startX, startY, translateX = 0, translateY = 0; + +// Play/Pause functionality +playPauseBtn.addEventListener('click', () => { + if (video.paused) { + video.play(); + playPauseBtn.textContent = '⏸️'; + } else { + video.pause(); + playPauseBtn.textContent = '▶️'; + } +}); + +// Fullscreen functionality +fullscreenBtn.addEventListener('click', () => { + if (!document.fullscreenElement) { + video.requestFullscreen(); + } else { + document.exitFullscreen(); + } +}); + +// Seek functionality +seekSlider.addEventListener('input', () => { + const seekTime = video.duration * (seekSlider.value / 100); + video.currentTime = seekTime; +}); + +video.addEventListener('timeupdate', () => { + const value = (100 / video.duration) * video.currentTime; + seekSlider.value = value; +}); + +// Double tap to skip +let lastTap = 0; +video.addEventListener('touchend', (e) => { + const currentTime = new Date().getTime(); + const tapLength = currentTime - lastTap; + + if (tapLength < 300 && tapLength > 0) { + const skipTime = e.touches[0].clientX < window.innerWidth / 2 ? -10 : 10; + video.currentTime += skipTime; + } + + lastTap = currentTime; +}); + +// Handle orientation changes +window.addEventListener('orientationchange', () => { + if (isZoomed) { + resetZoom(); + } +}); + +// Zoom and pan functionality +video.addEventListener('touchstart', (e) => { + if (e.touches.length === 2) { + isZoomed = true; + startX = (e.touches[0].pageX + e.touches[1].pageX) / 2; + startY = (e.touches[0].pageY + e.touches[1].pageY) / 2; + } +}); + +video.addEventListener('touchmove', (e) => { + if (isZoomed && e.touches.length === 2) { + const scale = Math.hypot( + e.touches[0].pageX - e.touches[1].pageX, + e.touches[0].pageY - e.touches[1].pageY + ) / Math.hypot( + e.touches[0].pageX - e.touches[1].pageX, + e.touches[0].pageY - e.touches[1].pageY + ); + + const currentX = (e.touches[0].pageX + e.touches[1].pageX) / 2; + const currentY = (e.touches[0].pageY + e.touches[1].pageY) / 2; + + translateX += currentX - startX; + translateY += currentY - startY; + + video.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`; + video.style.transformOrigin = 'center center'; + } +}); + +video.addEventListener('touchend', () => { + if (isZoomed) { + isZoomed = false; + } +}); + +function resetZoom() { + video.style.transform = 'none'; + translateX = 0; + translateY = 0; +} \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..2bc0aec --- /dev/null +++ b/style.css @@ -0,0 +1,127 @@ +:root { + --primary-color: #1e90ff; + --control-bg: rgba(0, 0, 0, 0.7); + --control-size: 40px; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', sans-serif; + background: #000; + color: #fff; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.video-container { + width: 100%; + max-width: 800px; + position: relative; + overflow: hidden; + border-radius: 15px; + box-shadow: 0 0 20px rgba(255, 255, 255, 0.1); +} + +.video-wrapper { + position: relative; + padding-top: 56.25%; +} + +video { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; +} + +.controls { + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: var(--control-bg); + padding: 10px; + display: flex; + align-items: center; + gap: 10px; + opacity: 0; + transition: opacity 0.3s ease; +} + +.video-container:hover .controls { + opacity: 1; +} + +.control-btn { + background: none; + border: none; + color: #fff; + font-size: 1.2rem; + cursor: pointer; + padding: 5px; + transition: transform 0.2s ease; +} + +.control-btn:hover { + transform: scale(1.1); +} + +.seek-bar { + flex: 1; +} + +#seek-slider { + width: 100%; + cursor: pointer; +} + +.orientation-notice { + margin-top: 20px; + text-align: center; + font-size: 1.1rem; + color: #aaa; +} + +.instructions { + margin-top: 30px; + padding: 20px; + background: rgba(255, 255, 255, 0.05); + border-radius: 10px; + max-width: 800px; +} + +.instructions h2 { + margin-bottom: 15px; + color: var(--primary-color); +} + +.instructions ul { + list-style: none; + padding: 0; +} + +.instructions li { + margin: 10px 0; + font-size: 1rem; + color: #ddd; +} + +@media (max-width: 768px) { + .video-container { + border-radius: 0; + } + + .instructions { + margin: 20px; + } +} \ No newline at end of file