Add video scrubbing and sound preview for interactive browsing
Implement mouse-based video scrubbing and low-volume audio playback for previews in the video card component. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 2eb1084e-b728-4449-9231-f1665924c8d5 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/2eb1084e-b728-4449-9231-f1665924c8d5/P3O2FU7
This commit is contained in:
parent
f1331dafc6
commit
05355d44fe
@ -51,31 +51,18 @@ export default function VideoCard({ video, onClick, className = "" }: VideoCardP
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const hlsRef = useRef<any>(null);
|
||||
|
||||
// Create subtle hover sound
|
||||
const playHoverSound = () => {
|
||||
try {
|
||||
// Create a subtle audio context for hover sound
|
||||
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
|
||||
const oscillator = audioContext.createOscillator();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioContext.destination);
|
||||
|
||||
// Subtle high-pitched tone
|
||||
oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
|
||||
oscillator.frequency.exponentialRampToValueAtTime(1200, audioContext.currentTime + 0.1);
|
||||
|
||||
// Very quiet volume
|
||||
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
|
||||
gainNode.gain.linearRampToValueAtTime(0.02, audioContext.currentTime + 0.02);
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.15);
|
||||
|
||||
oscillator.start(audioContext.currentTime);
|
||||
oscillator.stop(audioContext.currentTime + 0.15);
|
||||
} catch (error) {
|
||||
// Silently fail if audio context not supported
|
||||
console.debug('Audio context not supported');
|
||||
// Handle mouse scrubbing for video preview
|
||||
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (!showPreview || !videoRef.current) return;
|
||||
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const progress = Math.max(0, Math.min(1, x / rect.width));
|
||||
|
||||
// Scrub video based on mouse position
|
||||
if (videoRef.current.duration) {
|
||||
const targetTime = progress * videoRef.current.duration;
|
||||
videoRef.current.currentTime = targetTime;
|
||||
}
|
||||
};
|
||||
|
||||
@ -131,6 +118,9 @@ export default function VideoCard({ video, onClick, className = "" }: VideoCardP
|
||||
|
||||
hlsRef.current.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
console.log('HLS manifest parsed, starting playback');
|
||||
// Enable audio and play
|
||||
videoElement.muted = false;
|
||||
videoElement.volume = 0.3; // Low volume for preview
|
||||
videoElement.play().catch(e => console.log('Autoplay failed:', e));
|
||||
});
|
||||
|
||||
@ -140,6 +130,8 @@ export default function VideoCard({ video, onClick, className = "" }: VideoCardP
|
||||
} else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
// Safari native HLS support
|
||||
videoElement.src = video.videoUrl;
|
||||
videoElement.muted = false;
|
||||
videoElement.volume = 0.3;
|
||||
videoElement.play().catch(e => console.log('Autoplay failed:', e));
|
||||
}
|
||||
}
|
||||
@ -156,16 +148,14 @@ export default function VideoCard({ video, onClick, className = "" }: VideoCardP
|
||||
<div
|
||||
data-testid={`card-video-${video.id}`}
|
||||
className={`video-card transition-transform duration-200 hover:scale-[1.02] p-1 md:p-2 ${className}`}
|
||||
onMouseEnter={() => {
|
||||
setIsHovered(true);
|
||||
playHoverSound();
|
||||
}}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
{/* Video preview container */}
|
||||
<div
|
||||
className="relative gradient-card rounded-lg overflow-hidden mb-2 aspect-[9/16] md:aspect-[16/9] cursor-pointer group"
|
||||
onClick={() => onClick?.(video)}
|
||||
onMouseMove={handleMouseMove}
|
||||
>
|
||||
{/* Static thumbnail - always visible */}
|
||||
<img
|
||||
@ -205,10 +195,11 @@ export default function VideoCard({ video, onClick, className = "" }: VideoCardP
|
||||
objectPosition: video.faceCenterPosition || 'center center',
|
||||
objectFit: 'cover'
|
||||
}}
|
||||
muted
|
||||
muted={false} // Enable audio for preview
|
||||
loop
|
||||
playsInline
|
||||
preload="none"
|
||||
volume={0.3}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user