Skip to content

Commit d341be4

Browse files
fix: try to solve the layout shift problem
1 parent bc4c207 commit d341be4

2 files changed

Lines changed: 24 additions & 101 deletions

File tree

components/Testimonials.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const TestimonialContent: React.FC<{ content: React.ReactNode; media?: Testimoni
7070
{media && (
7171
<div className="sj-attachment-container overflow-hidden rounded-lg">
7272
{media.type === 'video' ? (
73-
<video controls className="sj-media w-full h-auto">
73+
<video controls className="sj-media w-full h-auto" preload="none">
7474
<source src={media.url} type="video/mp4" />
7575
</video>
7676
) : (

components/VideoComposite.tsx

Lines changed: 23 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,12 @@
11
'use client'
22

3-
import { useEffect, useRef, useState } from 'react'
3+
import { useEffect, useRef } from 'react'
44

55
export default function VideoComposite() {
66
const terminalVideoRef = useRef<HTMLVideoElement>(null)
77
const simVideoRef = useRef<HTMLVideoElement>(null)
8-
const [terminalVideoLoaded, setTerminalVideoLoaded] = useState(false)
9-
const [simVideoLoaded, setSimVideoLoaded] = useState(false)
10-
const [videosReady, setVideosReady] = useState(false)
118

129
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-
5310
const terminalVideo = terminalVideoRef.current
5411
const simVideo = simVideoRef.current
5512

@@ -88,71 +45,37 @@ export default function VideoComposite() {
8845
terminalVideo.removeEventListener('timeupdate', syncTimePosition)
8946
}
9047
}
91-
}, [videosReady])
48+
}, [])
9249

9350
return (
9451
<section className="max-w-[75ch] mx-auto px-2.5 py-4 sm:pl-[1ch] sm:pr-0">
9552
{/* Scalable container - 70% scale on desktop */}
9653
<div className="relative mx-auto aspect-[600/670] sm:scale-[0.7] sm:origin-top sm:-mb-[30%]">
9754
{/* 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+
/>
12666
</div>
12767

12868
{/* 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+
/>
15679
</div>
15780
</div>
15881
</section>

0 commit comments

Comments
 (0)