forked from 77media/video-flow
添加播放声音
This commit is contained in:
parent
aa5be1d5ff
commit
adda2077e1
@ -1,7 +1,7 @@
|
||||
'use client'; // Add this to ensure it's a client component
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Edit2, Trash2, Play } from 'lucide-react';
|
||||
import { Edit2, Trash2, Play, Volume2, VolumeX } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
@ -19,6 +19,7 @@ interface VideoGridLayoutProps {
|
||||
function VideoGridLayoutComponent({ videos, onEdit, onDelete }: VideoGridLayoutProps) {
|
||||
const [hoveredId, setHoveredId] = useState<string | null>(null);
|
||||
const [isPlaying, setIsPlaying] = useState<{ [key: string]: boolean }>({});
|
||||
const [isMuted, setIsMuted] = useState<{ [key: string]: boolean }>({});
|
||||
|
||||
const handleMouseEnter = (id: string) => {
|
||||
setHoveredId(id);
|
||||
@ -26,11 +27,13 @@ function VideoGridLayoutComponent({ videos, onEdit, onDelete }: VideoGridLayoutP
|
||||
|
||||
const handleMouseLeave = (id: string) => {
|
||||
setHoveredId(null);
|
||||
// 暂停视频
|
||||
// 暂停视频并重新静音以便下次预览
|
||||
const video = document.getElementById(`video-${id}`) as HTMLVideoElement;
|
||||
if (video) {
|
||||
video.pause();
|
||||
video.muted = true;
|
||||
setIsPlaying(prev => ({ ...prev, [id]: false }));
|
||||
setIsMuted(prev => ({ ...prev, [id]: true }));
|
||||
}
|
||||
};
|
||||
|
||||
@ -38,8 +41,11 @@ function VideoGridLayoutComponent({ videos, onEdit, onDelete }: VideoGridLayoutP
|
||||
const video = document.getElementById(`video-${id}`) as HTMLVideoElement;
|
||||
if (video) {
|
||||
if (video.paused) {
|
||||
// 在用户主动播放时取消静音
|
||||
video.muted = false;
|
||||
video.play();
|
||||
setIsPlaying(prev => ({ ...prev, [id]: true }));
|
||||
setIsMuted(prev => ({ ...prev, [id]: false }));
|
||||
} else {
|
||||
video.pause();
|
||||
setIsPlaying(prev => ({ ...prev, [id]: false }));
|
||||
@ -47,6 +53,15 @@ function VideoGridLayoutComponent({ videos, onEdit, onDelete }: VideoGridLayoutP
|
||||
}
|
||||
};
|
||||
|
||||
const toggleMute = (id: string, event: React.MouseEvent) => {
|
||||
event.stopPropagation(); // 防止触发父元素的点击事件
|
||||
const video = document.getElementById(`video-${id}`) as HTMLVideoElement;
|
||||
if (video) {
|
||||
video.muted = !video.muted;
|
||||
setIsMuted(prev => ({ ...prev, [id]: video.muted }));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 p-6">
|
||||
{videos.map((video) => (
|
||||
@ -64,7 +79,7 @@ function VideoGridLayoutComponent({ videos, onEdit, onDelete }: VideoGridLayoutP
|
||||
suppressHydrationWarning
|
||||
className="w-full h-full object-cover"
|
||||
loop
|
||||
muted
|
||||
muted={isMuted[video.id] !== false} // 默认静音,除非明确设置为false
|
||||
playsInline
|
||||
/>
|
||||
|
||||
@ -86,6 +101,19 @@ function VideoGridLayoutComponent({ videos, onEdit, onDelete }: VideoGridLayoutP
|
||||
${hoveredId === video.id ? 'translate-y-0 opacity-100' : 'translate-y-[-10px] opacity-0'}
|
||||
`}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="w-8 h-8 rounded-full bg-black/40 hover:bg-black/60 backdrop-blur-sm"
|
||||
onClick={(e) => toggleMute(video.id, e)}
|
||||
title={isMuted[video.id] !== false ? "取消静音" : "静音"}
|
||||
>
|
||||
{isMuted[video.id] !== false ? (
|
||||
<VolumeX className="w-4 h-4 text-white" />
|
||||
) : (
|
||||
<Volume2 className="w-4 h-4 text-white" />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'; // Add this to ensure it's a client component
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { ChevronLeft, ChevronRight, Volume2, VolumeX } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
@ -16,8 +16,17 @@ interface VideoScreenLayoutProps {
|
||||
function VideoScreenLayoutComponent({ videos }: VideoScreenLayoutProps) {
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [isAnimating, setIsAnimating] = useState(false);
|
||||
const [isMuted, setIsMuted] = useState(true); // 默认静音
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// 初始化时同步第一个视频的状态
|
||||
useEffect(() => {
|
||||
const currentVideo = document.getElementById(`video-screen-${currentIndex}`) as HTMLVideoElement;
|
||||
if (currentVideo) {
|
||||
setIsMuted(currentVideo.muted);
|
||||
}
|
||||
}, [currentIndex]);
|
||||
|
||||
// 计算每个面板的样式
|
||||
const getPanelStyle = (index: number) => {
|
||||
const position = index - currentIndex;
|
||||
@ -44,6 +53,15 @@ function VideoScreenLayoutComponent({ videos }: VideoScreenLayoutProps) {
|
||||
};
|
||||
};
|
||||
|
||||
// 切换静音状态
|
||||
const toggleMute = () => {
|
||||
const currentVideo = document.getElementById(`video-screen-${currentIndex}`) as HTMLVideoElement;
|
||||
if (currentVideo) {
|
||||
currentVideo.muted = !currentVideo.muted;
|
||||
setIsMuted(currentVideo.muted);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理切换
|
||||
const handleSlide = (direction: 'prev' | 'next') => {
|
||||
if (isAnimating) return;
|
||||
@ -55,8 +73,15 @@ function VideoScreenLayoutComponent({ videos }: VideoScreenLayoutProps) {
|
||||
|
||||
setCurrentIndex(newIndex);
|
||||
|
||||
// 动画结束后重置状态
|
||||
setTimeout(() => setIsAnimating(false), 500);
|
||||
// 动画结束后重置状态并同步新视频的静音状态
|
||||
setTimeout(() => {
|
||||
setIsAnimating(false);
|
||||
// 同步新视频的静音状态到UI
|
||||
const newVideo = document.getElementById(`video-screen-${newIndex}`) as HTMLVideoElement;
|
||||
if (newVideo) {
|
||||
setIsMuted(newVideo.muted);
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -79,20 +104,41 @@ function VideoScreenLayoutComponent({ videos }: VideoScreenLayoutProps) {
|
||||
<div className="relative w-full h-full overflow-hidden rounded-lg">
|
||||
{/* 视频 - Add suppressHydrationWarning to prevent className mismatch warnings */}
|
||||
<video
|
||||
id={`video-screen-${index}`}
|
||||
src={video.url}
|
||||
suppressHydrationWarning
|
||||
className="w-full h-full object-cover"
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
muted={index === currentIndex ? isMuted : true} // 只有当前视频受状态控制
|
||||
playsInline
|
||||
/>
|
||||
|
||||
{/* 视频标题 - 只在中间面板显示 */}
|
||||
{/* 视频标题和控制 - 只在中间面板显示 */}
|
||||
{index === currentIndex && (
|
||||
<div className="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/80 to-transparent">
|
||||
<h3 className="text-white text-lg font-medium">{video.title}</h3>
|
||||
</div>
|
||||
<>
|
||||
{/* 音量控制按钮 */}
|
||||
<div className="absolute top-4 right-4">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="w-10 h-10 rounded-full bg-black/40 hover:bg-black/60 backdrop-blur-sm"
|
||||
onClick={toggleMute}
|
||||
title={isMuted ? "取消静音" : "静音"}
|
||||
>
|
||||
{isMuted ? (
|
||||
<VolumeX className="w-5 h-5 text-white" />
|
||||
) : (
|
||||
<Volume2 className="w-5 h-5 text-white" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 视频标题 */}
|
||||
<div className="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/80 to-transparent">
|
||||
<h3 className="text-white text-lg font-medium">{video.title}</h3>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 玻璃态遮罩 - 侧面板半透明效果 */}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user