声音加开关

This commit is contained in:
Xin Wang 2025-07-03 07:35:13 +08:00
parent f6eea4afe1
commit 561516bc0c

View File

@ -1,8 +1,8 @@
'use client';
import React, { useRef, useEffect } from 'react';
import React, { useRef, useEffect, useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Edit3, Play, Pause } from 'lucide-react';
import { Edit3, Play, Pause, Volume2, VolumeX } from 'lucide-react';
import { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal';
import { GlassIconButton } from '@/components/ui/glass-icon-button';
@ -40,10 +40,45 @@ export function MediaViewer({
final
}: MediaViewerProps) {
const mainVideoRef = useRef<HTMLVideoElement>(null);
const finalVideoRef = useRef<HTMLVideoElement>(null);
// 音量控制状态
const [isMuted, setIsMuted] = useState(false);
const [volume, setVolume] = useState(0.8);
// 音量控制函数
const toggleMute = () => {
setIsMuted(!isMuted);
if (mainVideoRef.current) {
mainVideoRef.current.muted = !isMuted;
}
if (finalVideoRef.current) {
finalVideoRef.current.muted = !isMuted;
}
};
const handleVolumeChange = (newVolume: number) => {
setVolume(newVolume);
if (mainVideoRef.current) {
mainVideoRef.current.volume = newVolume;
}
if (finalVideoRef.current) {
finalVideoRef.current.volume = newVolume;
}
};
// 应用音量设置到视频元素
const applyVolumeSettings = (videoElement: HTMLVideoElement) => {
if (videoElement) {
videoElement.volume = volume;
videoElement.muted = isMuted;
}
};
// 视频播放控制
useEffect(() => {
if (mainVideoRef.current) {
applyVolumeSettings(mainVideoRef.current);
if (isVideoPlaying) {
mainVideoRef.current.play().catch(error => {
console.log('视频播放失败:', error);
@ -57,6 +92,7 @@ export function MediaViewer({
// 当切换视频时重置视频播放
useEffect(() => {
if (mainVideoRef.current) {
applyVolumeSettings(mainVideoRef.current);
mainVideoRef.current.currentTime = 0;
if (isVideoPlaying) {
mainVideoRef.current.play().catch(error => {
@ -66,6 +102,56 @@ export function MediaViewer({
}
}, [currentSketchIndex]);
// 音量设置变化时应用到所有视频
useEffect(() => {
if (mainVideoRef.current) {
applyVolumeSettings(mainVideoRef.current);
}
if (finalVideoRef.current) {
applyVolumeSettings(finalVideoRef.current);
}
}, [volume, isMuted]);
// 渲染音量控制组件
const renderVolumeControls = () => (
<div className="flex items-center gap-2">
{/* 静音按钮 */}
<GlassIconButton
icon={isMuted ? VolumeX : Volume2}
tooltip={isMuted ? "取消静音" : "静音"}
onClick={toggleMute}
size="sm"
/>
{/* 音量滑块 - 一直显示 */}
<div className="flex items-center gap-2">
<div className="relative">
<input
type="range"
min="0"
max="1"
step="0.1"
value={volume}
onChange={(e) => handleVolumeChange(parseFloat(e.target.value))}
className="w-16 h-1 bg-white/20 rounded-lg appearance-none cursor-pointer
[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3
[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white
[&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:shadow-lg
[&::-moz-range-thumb]:w-3 [&::-moz-range-thumb]:h-3 [&::-moz-range-thumb]:rounded-full
[&::-moz-range-thumb]:bg-white [&::-moz-range-thumb]:cursor-pointer
[&::-moz-range-thumb]:border-none [&::-moz-range-thumb]:shadow-lg"
style={{
background: `linear-gradient(to right, white 0%, white ${volume * 100}%, rgba(255,255,255,0.2) ${volume * 100}%, rgba(255,255,255,0.2) 100%)`
}}
/>
</div>
<span className="text-xs text-white/70 w-8 text-center">
{Math.round(volume * 100)}%
</span>
</div>
</div>
);
// 渲染最终成片
const renderFinalVideo = () => {
// 使用真实的final数据如果没有则使用默认值
@ -108,12 +194,14 @@ export function MediaViewer({
className="relative z-10"
>
<video
ref={finalVideoRef}
className="w-full h-full object-cover rounded-lg"
src={finalVideo.url}
poster={taskSketch[currentSketchIndex]?.url}
autoPlay
loop
playsInline
onLoadedData={() => applyVolumeSettings(finalVideoRef.current!)}
/>
</motion.div>
@ -162,6 +250,16 @@ export function MediaViewer({
</div>
</motion.div>
{/* 底部音量控制 */}
<motion.div
className="absolute bottom-4 left-4 z-10 flex items-center gap-3"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 1, duration: 0.6 }}
>
{renderVolumeControls()}
</motion.div>
{/* 完成标记 */}
<motion.div
className="absolute top-4 right-4 px-3 py-1.5 rounded-full bg-emerald-500/20 backdrop-blur-sm
@ -253,6 +351,7 @@ export function MediaViewer({
autoPlay={isVideoPlaying}
loop={true}
playsInline
onLoadedData={() => applyVolumeSettings(mainVideoRef.current!)}
onEnded={() => {
if (isVideoPlaying) {
// 自动切换到下一个视频的逻辑在父组件处理
@ -292,6 +391,7 @@ export function MediaViewer({
autoPlay={isVideoPlaying}
loop={true}
playsInline
onLoadedData={() => applyVolumeSettings(mainVideoRef.current!)}
onEnded={() => {
if (isVideoPlaying) {
// 自动切换到下一个视频的逻辑在父组件处理
@ -320,15 +420,16 @@ export function MediaViewer({
)}
</AnimatePresence>
{/* 底部播放按钮 */}
{/* 底部控制区域 */}
<AnimatePresence>
<motion.div
className="absolute bottom-4 left-4 z-[11]"
className="absolute bottom-4 left-4 z-[11] flex items-center gap-3"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.2 }}
>
{/* 播放按钮 */}
<motion.div
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
@ -356,6 +457,9 @@ export function MediaViewer({
size="sm"
/>
</motion.div>
{/* 音量控制 */}
{renderVolumeControls()}
</motion.div>
</AnimatePresence>
</div>