|
1 | 1 | 'use client' |
2 | 2 |
|
3 | | -import { useEffect, useRef, useState } from 'react' |
| 3 | +import { useEffect, useRef } from 'react' |
4 | 4 |
|
5 | 5 | export default function VideoComposite() { |
6 | 6 | const terminalVideoRef = useRef<HTMLVideoElement>(null) |
7 | 7 | const simVideoRef = useRef<HTMLVideoElement>(null) |
8 | | - const [terminalVideoLoaded, setTerminalVideoLoaded] = useState(false) |
9 | | - const [simVideoLoaded, setSimVideoLoaded] = useState(false) |
10 | | - const [videosReady, setVideosReady] = useState(false) |
11 | 8 |
|
12 | 9 | useEffect(() => { |
13 | | - // Preload videos asynchronously |
14 | | - const preloadVideos = async () => { |
15 | | - const terminalVideo = document.createElement('video') |
16 | | - const simVideo = document.createElement('video') |
17 | | - |
18 | | - terminalVideo.src = '/take-4-short-term.mp4' |
19 | | - terminalVideo.muted = true |
20 | | - terminalVideo.playsInline = true |
21 | | - |
22 | | - simVideo.src = '/take-4-short-sim.webm' |
23 | | - simVideo.muted = true |
24 | | - simVideo.playsInline = true |
25 | | - |
26 | | - // Wait for both videos to be ready |
27 | | - await Promise.all([ |
28 | | - new Promise(resolve => { |
29 | | - terminalVideo.addEventListener('canplaythrough', () => { |
30 | | - setTerminalVideoLoaded(true) |
31 | | - resolve(true) |
32 | | - }) |
33 | | - terminalVideo.load() |
34 | | - }), |
35 | | - new Promise(resolve => { |
36 | | - simVideo.addEventListener('canplaythrough', () => { |
37 | | - setSimVideoLoaded(true) |
38 | | - resolve(true) |
39 | | - }) |
40 | | - simVideo.load() |
41 | | - }) |
42 | | - ]) |
43 | | - |
44 | | - setVideosReady(true) |
45 | | - } |
46 | | - |
47 | | - preloadVideos() |
48 | | - }, []) |
49 | | - |
50 | | - useEffect(() => { |
51 | | - if (!videosReady) return |
52 | | - |
53 | 10 | const terminalVideo = terminalVideoRef.current |
54 | 11 | const simVideo = simVideoRef.current |
55 | 12 |
|
@@ -88,71 +45,37 @@ export default function VideoComposite() { |
88 | 45 | terminalVideo.removeEventListener('timeupdate', syncTimePosition) |
89 | 46 | } |
90 | 47 | } |
91 | | - }, [videosReady]) |
| 48 | + }, []) |
92 | 49 |
|
93 | 50 | return ( |
94 | 51 | <section className="max-w-[75ch] mx-auto px-2.5 py-4 sm:pl-[1ch] sm:pr-0"> |
95 | 52 | {/* Scalable container - 70% scale on desktop */} |
96 | 53 | <div className="relative mx-auto aspect-[600/670] sm:scale-[0.7] sm:origin-top sm:-mb-[30%]"> |
97 | 54 | {/* Terminal video - positioned top-left */} |
98 | | - <div className="absolute top-0 left-0 h-[74.6%] aspect-[500/512] bg-gray-900 text-gray-300 rounded-xl overflow-hidden shadow-2xl flex items-center justify-center"> |
99 | | - {!videosReady ? ( |
100 | | - <> |
101 | | - {/* Poster image with fixed dimensions to prevent layout shift */} |
102 | | - <img |
103 | | - src="/take-4-short-term-poster.png" |
104 | | - alt="Terminal demo" |
105 | | - className="w-full h-full object-cover" |
106 | | - width={500} |
107 | | - height={512} |
108 | | - /> |
109 | | - {/* Loading indicator */} |
110 | | - <div className="absolute inset-0 flex items-center justify-center bg-black bg-opacity-50"> |
111 | | - <div className="w-16 h-16 border-4 border-gray-300 border-t-transparent rounded-full animate-spin"></div> |
112 | | - </div> |
113 | | - </> |
114 | | - ) : ( |
115 | | - <video |
116 | | - ref={terminalVideoRef} |
117 | | - src="/take-4-short-term.mp4" |
118 | | - poster="/take-4-short-term-poster.png" |
119 | | - className="w-full h-full object-cover" |
120 | | - controls |
121 | | - loop |
122 | | - muted |
123 | | - playsInline |
124 | | - /> |
125 | | - )} |
| 55 | + <div className="absolute top-0 left-0 h-[74.6%] aspect-[500/512] bg-gray-900 text-gray-300 rounded-xl overflow-hidden shadow-2xl"> |
| 56 | + <video |
| 57 | + ref={terminalVideoRef} |
| 58 | + src="/take-4-short-term.mp4" |
| 59 | + poster="/take-4-short-term-poster.png" |
| 60 | + className="w-full h-full object-cover" |
| 61 | + controls |
| 62 | + loop |
| 63 | + muted |
| 64 | + playsInline |
| 65 | + /> |
126 | 66 | </div> |
127 | 67 |
|
128 | 68 | {/* Phone video - positioned bottom-right */} |
129 | | - <div className="absolute bottom-0 right-0 w-[50%] aspect-[1110/2232]"> |
130 | | - {!videosReady ? ( |
131 | | - <> |
132 | | - {/* Poster image with fixed dimensions to prevent layout shift */} |
133 | | - <img |
134 | | - src="/take-4-short-sim-poster.png" |
135 | | - alt="Mobile app demo" |
136 | | - className="w-full h-full object-cover" |
137 | | - width={1110} |
138 | | - height={2232} |
139 | | - /> |
140 | | - {/* Loading indicator */} |
141 | | - <div className="absolute inset-0 flex items-center justify-center bg-black bg-opacity-30 rounded-lg"> |
142 | | - <div className="w-8 h-8 border-2 border-white border-t-transparent rounded-full animate-spin"></div> |
143 | | - </div> |
144 | | - </> |
145 | | - ) : ( |
146 | | - <video |
147 | | - ref={simVideoRef} |
148 | | - src="/take-4-short-sim.webm" |
149 | | - poster="/take-4-short-sim-poster.png" |
150 | | - className="w-full h-full object-cover" |
151 | | - loop |
152 | | - muted |
153 | | - playsInline |
154 | | - /> |
155 | | - )} |
| 69 | + <div className="absolute bottom-0 right-0 w-[50%]"> |
| 70 | + <video |
| 71 | + ref={simVideoRef} |
| 72 | + src="/take-4-short-sim.webm" |
| 73 | + poster="/take-4-short-sim-poster.png" |
| 74 | + className="w-full h-full object-cover" |
| 75 | + loop |
| 76 | + muted |
| 77 | + playsInline |
| 78 | + /> |
156 | 79 | </div> |
157 | 80 | </div> |
158 | 81 | </section> |
|
0 commit comments