fixed transition smoothing issue for presentations

This commit is contained in:
Dmitri 2026-03-25 13:18:03 +04:00
parent 7f3b1795af
commit c25e7cdcc2

View File

@ -24,7 +24,7 @@ import LayoutGuest from '../layouts/Guest';
import { getPageTitle } from '../config';
import { PRELOAD_CONFIG } from '../config/preload.config';
import { usePreloadOrchestrator } from '../hooks/usePreloadOrchestrator';
import { useReversePlayback } from '../hooks/useReversePlayback';
import { useTransitionPlayback } from '../hooks/useTransitionPlayback';
import { logger } from '../lib/logger';
import { resolveAssetPlaybackUrl } from '../lib/assetUrl';
import { buildElementStyle } from '../lib/elementStyles';
@ -32,7 +32,6 @@ import type {
RuntimeProject,
RuntimePage,
RuntimePageLink,
TransitionOverlayState,
} from '../types/runtime';
interface RuntimePresentationProps {
@ -133,13 +132,16 @@ export default function RuntimePresentation({
const [pageLinks, setPageLinks] = useState<RuntimePageLink[]>([]);
const [selectedPageId, setSelectedPageId] = useState<string | null>(null);
const [pageHistory, setPageHistory] = useState<string[]>([]);
const [overlayTransition, setOverlayTransition] =
useState<TransitionOverlayState | null>(null);
const [transitionPreview, setTransitionPreview] = useState<{
targetPageId: string;
videoUrl: string;
isReverse: boolean;
} | null>(null);
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(true);
const [isFullscreen, setIsFullscreen] = useState(false);
const overlayVideoRef = useRef<HTMLVideoElement | null>(null);
const transitionVideoRef = useRef<HTMLVideoElement>(null);
// API request config with custom headers for project/environment
const apiConfig = useMemo(
@ -212,6 +214,50 @@ export default function RuntimePresentation({
enabled: !isLoading && !error,
});
// Integrate useTransitionPlayback hook for smooth transitions (matches Constructor pattern)
const { isBuffering } = useTransitionPlayback({
videoRef: transitionVideoRef,
transition: transitionPreview
? {
videoUrl: transitionPreview.videoUrl,
reverseMode: transitionPreview.isReverse ? 'reverse' : 'none',
targetPageId: transitionPreview.targetPageId,
displayName: 'Transition',
}
: null,
onComplete: (targetPageId) => {
if (targetPageId) {
const targetPage = pages.find((p) => p.id === targetPageId);
waitForPageImages(targetPage || null).then(() => {
setSelectedPageId(targetPageId);
setPageHistory((prev) => [...prev, targetPageId]);
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setTransitionPreview(null);
});
});
});
}
},
features: {
useBlobUrl: true,
preDecodeImages: true,
getTargetPageImages: () => {
if (!transitionPreview?.targetPageId) return [];
const targetPage = pages.find(
(p) => p.id === transitionPreview.targetPageId,
);
if (!targetPage?.background_image_url) return [];
const url = resolveAssetPlaybackUrl(targetPage.background_image_url);
return url ? [url] : [];
},
},
preload: {
preloadedUrls: preloadOrchestrator?.preloadedUrls || new Set(),
getCachedBlobUrl: preloadOrchestrator?.getCachedBlobUrl,
},
});
const toggleFullscreen = useCallback(async () => {
try {
if (!document.fullscreenElement) {
@ -361,10 +407,9 @@ export default function RuntimePresentation({
if (!targetPage) return;
if (transitionVideoUrl) {
// Play transition (forward or reverse)
setOverlayTransition({
// Play transition using useTransitionPlayback hook
setTransitionPreview({
targetPageId,
transitionName: 'Transition',
videoUrl: resolveAssetPlaybackUrl(transitionVideoUrl),
isReverse: isBack,
});
@ -391,67 +436,6 @@ export default function RuntimePresentation({
[navigateToPage],
);
const finishOverlayTransition = useCallback(async () => {
if (!overlayTransition) return;
const targetPage = pages.find(
(p) => p.id === overlayTransition.targetPageId,
);
// Wait for images while showing last frame
await waitForPageImages(targetPage || null);
// Switch page
setSelectedPageId(overlayTransition.targetPageId);
setPageHistory((prev) => [...prev, overlayTransition.targetPageId]);
// Hide transition after React renders
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setOverlayTransition(null);
});
});
}, [overlayTransition, pages]);
// Use the reverse playback hook (same as constructor.tsx)
const { startReverse, stopReverse } = useReversePlayback({
videoRef: overlayVideoRef,
onComplete: finishOverlayTransition,
preloadedUrls: preloadOrchestrator.preloadedUrls,
videoUrl: overlayTransition?.videoUrl,
getCachedBlobUrl: preloadOrchestrator.getCachedBlobUrl,
});
// Handle reverse playback when transition starts in reverse mode
useEffect(() => {
if (!overlayTransition?.isReverse) return;
const video = overlayVideoRef.current;
if (!video) return;
// Start reverse when video data is ready (preloaded assets load instantly)
const handleLoadedData = () => {
startReverse();
};
if (video.readyState >= 2) {
handleLoadedData();
} else {
video.addEventListener('loadeddata', handleLoadedData, { once: true });
}
return () => {
stopReverse();
video.removeEventListener('loadeddata', handleLoadedData);
};
}, [
overlayTransition?.isReverse,
overlayTransition?.videoUrl,
overlayTransition?.durationSec,
startReverse,
stopReverse,
]);
// Render element content based on type
const renderElementContent = (element: any) => {
// Navigation buttons
@ -769,23 +753,17 @@ export default function RuntimePresentation({
</span>
</div>
{/* Transition overlay */}
{overlayTransition && (
<div className='absolute inset-0 z-40 bg-black'>
{/* Transition overlay - uses useTransitionPlayback hook for smooth transitions */}
{transitionPreview && (
<div className='fixed inset-0 z-50 overflow-hidden pointer-events-none'>
<video
ref={overlayVideoRef}
className='w-full h-full object-cover'
src={overlayTransition.videoUrl}
autoPlay={!overlayTransition.isReverse}
ref={transitionVideoRef}
className='absolute inset-0 h-full w-full object-cover transition-opacity duration-300 ease-linear'
style={{ opacity: isBuffering ? 0 : 1 }}
muted
playsInline
preload='auto'
onEnded={
overlayTransition.isReverse
? undefined
: finishOverlayTransition
}
onError={finishOverlayTransition}
disablePictureInPicture
/>
</div>
)}