import { Pause, Play, RotateCcw } from "lucide-react" import { useEffect, useRef, useState } from "react" import { useTranslation } from "react-i18next" import { Button } from "@/components/ui/button" import { Slider } from "@/components/ui/slider" import { VideoEffect } from "@/features/effects/types" import { generateCSSFilterForEffect, getPlaybackRate } from "../utils/css-effects" interface EffectComparisonProps { effect: VideoEffect videoPath?: string customParams?: Record width?: number height?: number } /** * Компонент для сравнения видео до и после применения эффекта * Показывает разделенный экран с возможностью перемещения разделителя */ export function EffectComparison({ effect, videoPath = "/t1.mp4", customParams, width = 600, height = 400, }: EffectComparisonProps) { const { t } = useTranslation() const [splitPosition, setSplitPosition] = useState(50) const [isPlaying, setIsPlaying] = useState(false) const [isDragging, setIsDragging] = useState(false) const originalVideoRef = useRef(null) const effectVideoRef = useRef(null) const containerRef = useRef(null) // Синхронизация воспроизведения видео useEffect(() => { const originalVideo = originalVideoRef.current const effectVideo = effectVideoRef.current if (!originalVideo || !effectVideo) return // Синхронизируем currentTime при изменении const syncTime = () => { if (Math.abs(originalVideo.currentTime - effectVideo.currentTime) > 0.1) { effectVideo.currentTime = originalVideo.currentTime } } originalVideo.addEventListener("timeupdate", syncTime) return () => { originalVideo.removeEventListener("timeupdate", syncTime) } }, []) // Применение эффекта к видео useEffect(() => { const effectVideo = effectVideoRef.current if (!effectVideo || !effect) return // Создаем эффект с объединенными параметрами const mergedEffect = customParams ? { ...effect, parameters: (effect.parameters || []).map((param) => ({ ...param, defaultValue: customParams[param.id] ?? param.defaultValue, })), } : effect // Применяем CSS фильтры const cssFilter = generateCSSFilterForEffect(mergedEffect) effectVideo.style.filter = cssFilter // Применяем скорость воспроизведения для эффектов движения const playbackRate = getPlaybackRate(mergedEffect) effectVideo.playbackRate = playbackRate }, [effect, customParams]) // Обработчик воспроизведения/паузы const handlePlayPause = () => { const originalVideo = originalVideoRef.current const effectVideo = effectVideoRef.current if (!originalVideo || !effectVideo) return if (isPlaying) { originalVideo.pause() effectVideo.pause() } else { void originalVideo.play() void effectVideo.play() } setIsPlaying(!isPlaying) } // Обработчик сброса const handleReset = () => { const originalVideo = originalVideoRef.current const effectVideo = effectVideoRef.current if (!originalVideo || !effectVideo) return originalVideo.currentTime = 0 effectVideo.currentTime = 0 originalVideo.pause() effectVideo.pause() setIsPlaying(false) setSplitPosition(50) } // Обработчик перемещения разделителя const handleMouseMove = (e: MouseEvent) => { if (!isDragging || !containerRef.current) return const rect = containerRef.current.getBoundingClientRect() const x = e.clientX - rect.left const percentage = (x / rect.width) * 100 setSplitPosition(Math.max(0, Math.min(100, percentage))) } // Обработчики мыши для перетаскивания useEffect(() => { const handleMouseUp = () => setIsDragging(false) if (isDragging) { window.addEventListener("mousemove", handleMouseMove) window.addEventListener("mouseup", handleMouseUp) } return () => { window.removeEventListener("mousemove", handleMouseMove) window.removeEventListener("mouseup", handleMouseUp) } }, [isDragging]) return (
{/* Контейнер для видео */}
setIsDragging(true)} > {/* Оригинальное видео (левая часть) */}
{/* Видео с эффектом (правая часть) */}
{/* Разделитель */}
{/* Контролы */}
{t("effects.comparison.position", "Позиция")}: setSplitPosition(value)} min={0} max={100} step={1} className="flex-1" /> {Math.round(splitPosition)}%
) }