forked from 77media/video-flow
声音加开关
This commit is contained in:
parent
f6eea4afe1
commit
561516bc0c
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user