forked from 77media/video-flow
声音加开关
This commit is contained in:
parent
f6eea4afe1
commit
561516bc0c
@ -1,8 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { useRef, useEffect } from 'react';
|
import React, { useRef, useEffect, useState } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
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 { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal';
|
||||||
import { GlassIconButton } from '@/components/ui/glass-icon-button';
|
import { GlassIconButton } from '@/components/ui/glass-icon-button';
|
||||||
|
|
||||||
@ -40,10 +40,45 @@ export function MediaViewer({
|
|||||||
final
|
final
|
||||||
}: MediaViewerProps) {
|
}: MediaViewerProps) {
|
||||||
const mainVideoRef = useRef<HTMLVideoElement>(null);
|
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(() => {
|
useEffect(() => {
|
||||||
if (mainVideoRef.current) {
|
if (mainVideoRef.current) {
|
||||||
|
applyVolumeSettings(mainVideoRef.current);
|
||||||
if (isVideoPlaying) {
|
if (isVideoPlaying) {
|
||||||
mainVideoRef.current.play().catch(error => {
|
mainVideoRef.current.play().catch(error => {
|
||||||
console.log('视频播放失败:', error);
|
console.log('视频播放失败:', error);
|
||||||
@ -57,6 +92,7 @@ export function MediaViewer({
|
|||||||
// 当切换视频时重置视频播放
|
// 当切换视频时重置视频播放
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mainVideoRef.current) {
|
if (mainVideoRef.current) {
|
||||||
|
applyVolumeSettings(mainVideoRef.current);
|
||||||
mainVideoRef.current.currentTime = 0;
|
mainVideoRef.current.currentTime = 0;
|
||||||
if (isVideoPlaying) {
|
if (isVideoPlaying) {
|
||||||
mainVideoRef.current.play().catch(error => {
|
mainVideoRef.current.play().catch(error => {
|
||||||
@ -66,6 +102,56 @@ export function MediaViewer({
|
|||||||
}
|
}
|
||||||
}, [currentSketchIndex]);
|
}, [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 = () => {
|
const renderFinalVideo = () => {
|
||||||
// 使用真实的final数据,如果没有则使用默认值
|
// 使用真实的final数据,如果没有则使用默认值
|
||||||
@ -108,12 +194,14 @@ export function MediaViewer({
|
|||||||
className="relative z-10"
|
className="relative z-10"
|
||||||
>
|
>
|
||||||
<video
|
<video
|
||||||
|
ref={finalVideoRef}
|
||||||
className="w-full h-full object-cover rounded-lg"
|
className="w-full h-full object-cover rounded-lg"
|
||||||
src={finalVideo.url}
|
src={finalVideo.url}
|
||||||
poster={taskSketch[currentSketchIndex]?.url}
|
poster={taskSketch[currentSketchIndex]?.url}
|
||||||
autoPlay
|
autoPlay
|
||||||
loop
|
loop
|
||||||
playsInline
|
playsInline
|
||||||
|
onLoadedData={() => applyVolumeSettings(finalVideoRef.current!)}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
@ -162,6 +250,16 @@ export function MediaViewer({
|
|||||||
</div>
|
</div>
|
||||||
</motion.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
|
<motion.div
|
||||||
className="absolute top-4 right-4 px-3 py-1.5 rounded-full bg-emerald-500/20 backdrop-blur-sm
|
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}
|
autoPlay={isVideoPlaying}
|
||||||
loop={true}
|
loop={true}
|
||||||
playsInline
|
playsInline
|
||||||
|
onLoadedData={() => applyVolumeSettings(mainVideoRef.current!)}
|
||||||
onEnded={() => {
|
onEnded={() => {
|
||||||
if (isVideoPlaying) {
|
if (isVideoPlaying) {
|
||||||
// 自动切换到下一个视频的逻辑在父组件处理
|
// 自动切换到下一个视频的逻辑在父组件处理
|
||||||
@ -292,6 +391,7 @@ export function MediaViewer({
|
|||||||
autoPlay={isVideoPlaying}
|
autoPlay={isVideoPlaying}
|
||||||
loop={true}
|
loop={true}
|
||||||
playsInline
|
playsInline
|
||||||
|
onLoadedData={() => applyVolumeSettings(mainVideoRef.current!)}
|
||||||
onEnded={() => {
|
onEnded={() => {
|
||||||
if (isVideoPlaying) {
|
if (isVideoPlaying) {
|
||||||
// 自动切换到下一个视频的逻辑在父组件处理
|
// 自动切换到下一个视频的逻辑在父组件处理
|
||||||
@ -320,15 +420,16 @@ export function MediaViewer({
|
|||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
{/* 底部播放按钮 */}
|
{/* 底部控制区域 */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
<motion.div
|
<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 }}
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
exit={{ opacity: 0, scale: 0.8 }}
|
exit={{ opacity: 0, scale: 0.8 }}
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
|
{/* 播放按钮 */}
|
||||||
<motion.div
|
<motion.div
|
||||||
whileHover={{ scale: 1.1 }}
|
whileHover={{ scale: 1.1 }}
|
||||||
whileTap={{ scale: 0.9 }}
|
whileTap={{ scale: 0.9 }}
|
||||||
@ -356,6 +457,9 @@ export function MediaViewer({
|
|||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
|
{/* 音量控制 */}
|
||||||
|
{renderVolumeControls()}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user