Animation Effects with Stage Flow
A demonstration of different animation effects during stage transitions using individual stage components and StageRenderer with advanced timer control features.
Key features to observe:
- Individual Stage Components: Each page is a separate React component
- StageRenderer Usage: Automatic stage rendering without conditional rendering
- Loading Stage: Initial loading animation with self-implemented timer (3 seconds)
- Page Navigation: Navigate between 5 different colored pages
- Built-in Effects: Using Stage Flow's built-in animation effects
- Navigation Controls: Previous/Next buttons and direct page selection
- Smooth Transitions: Observe how animations enhance the user experience
- State Management: Page state and animation state management
- Advanced Timer Control: Pause/resume timers on hover, reset functionality, and real-time remaining time display
- StageFlowEngine Integration: All timer functionality relies on the engine's internal timer mechanisms
Live Editor
// import { StageFlowEngine } from '@stage-flow/core'; // import { StageFlowProvider, useStageFlow, StageRenderer } from '@stage-flow/react'; function AnimationWithStageFlow() { // Common UI Components function PageContainer({ children, backgroundColor, color = "white", style = {}, onMouseEnter, onMouseLeave, onResetTimer }) { const containerStyle = { minHeight: "400px", display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", backgroundColor, color, borderRadius: "12px", boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)", margin: "20px", position: "relative", overflow: "hidden", ...style, }; const resetButtonStyle = { position: "absolute", bottom: "15px", right: "15px", padding: "6px 12px", fontSize: "12px", backgroundColor: "rgba(255, 255, 255, 0.2)", color: "white", border: "1px solid rgba(255, 255, 255, 0.3)", borderRadius: "4px", cursor: "pointer", backdropFilter: "blur(5px)", transition: "all 0.2s ease", }; return ( <div style={containerStyle} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}> {children} {onResetTimer && ( <button onClick={onResetTimer} style={resetButtonStyle} onMouseEnter={e => { e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.3)"; }} onMouseLeave={e => { e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.2)"; }} > Reset Timer </button> )} </div> ); } function PageTitle({ children, style = {} }) { const titleStyle = { fontSize: "48px", margin: "0 0 20px 0", ...style, }; return <h1 style={titleStyle}>{children}</h1>; } function PageDescription({ children, style = {} }) { const descStyle = { fontSize: "18px", margin: "0 0 30px 0", textAlign: "center", ...style, }; return <p style={descStyle}>{children}</p>; } function EffectInfo({ effect, color, style = {} }) { const infoStyle = { backgroundColor: "rgba(255, 255, 255, 0.2)", padding: "20px", borderRadius: "8px", textAlign: "center", ...style, }; return ( <div style={infoStyle}> <p style={{ margin: "0 0 10px 0", fontSize: "16px" }}> Effect: <strong>{effect}</strong> </p> <p style={{ margin: "0", fontSize: "14px", opacity: 0.9 }}>Color: {color}</p> </div> ); } function NavigationButton({ onClick, children, variant = "primary", style = {} }) { const buttonStyles = { primary: { backgroundColor: "#007bff", color: "white", }, secondary: { backgroundColor: "#6c757d", color: "white", }, }; const buttonStyle = { padding: "12px 24px", border: "none", borderRadius: "6px", cursor: "pointer", fontSize: "16px", ...buttonStyles[variant], ...style, }; return ( <button onClick={onClick} style={buttonStyle}> {children} </button> ); } function PageButton({ onClick, isActive, color, children, style = {} }) { const buttonStyle = { padding: "8px 16px", backgroundColor: isActive ? color : "#e9ecef", color: isActive ? "white" : "#495057", border: "none", borderRadius: "4px", cursor: "pointer", fontSize: "14px", transition: "all 0.2s ease", ...style, }; return ( <button onClick={onClick} style={buttonStyle}> {children} </button> ); } function TimerDisplay({ remainingTime, isPaused, style = {} }) { const timerStyle = { fontSize: "12px", color: "rgba(255,255,255,0.8)", marginTop: "10px", ...style, }; return ( <p style={timerStyle}> Stage Timer: {remainingTime}ms {isPaused ? "(Paused)" : ""} </p> ); } // Loading stage component function LoadingStage({ stage, data, send, goTo, isTransitioning }) { // Auto-transition from loading to page1 after 3 seconds // This is handled by the engine's built-in timer system return ( <PageContainer backgroundColor="#f8f9fa" color="#333"> <div style={{ width: "50px", height: "50px", border: "4px solid #f3f3f3", borderTop: "4px solid #007bff", borderRadius: "50%", animation: "spin 1s linear infinite", }} /> <p style={{ marginTop: "20px", fontSize: "18px" }}>Loading...</p> <p style={{ fontSize: "14px", color: "#666" }}>Initializing animation system...</p> <p style={{ fontSize: "12px", color: "#999", marginTop: "10px" }}>Initializing...</p> </PageContainer> ); } // Unified page stage component function PageStage({ stage, data, send, goTo, isTransitioning, page, remainingTime, isPaused, onMouseEnter, onMouseLeave, onResetTimer }) { return ( <PageContainer backgroundColor={page.color} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} onResetTimer={onResetTimer}> <PageTitle>{page.name}</PageTitle> <PageDescription> This page demonstrates the <strong>{page.effect}</strong> effect. </PageDescription> <EffectInfo effect={page.effect} color={page.color} /> <TimerDisplay remainingTime={remainingTime} isPaused={isPaused} /> </PageContainer> ); } // Create engine directly without useRef const engine = new StageFlowEngine({ initial: "loading", stages: [ { name: "loading", transitions: [{ target: "page1", event: "loaded" }], effect: "fade", onEnter: context => { // Auto-transition after 3 seconds const transitionTimer = setTimeout(() => { context.send("loaded"); }, 3000); // Store timer reference in context data context.data = { ...context.data, transitionTimer }; }, onExit: context => { // Clean up timer if (context.data?.transitionTimer) { clearTimeout(context.data.transitionTimer); } }, }, { name: "page1", transitions: [ { target: "page2", event: "next", after: 5000 }, { target: "page1", event: "previous" }, ], effect: "slideRight", }, { name: "page2", transitions: [ { target: "page3", event: "next", after: 5000 }, { target: "page1", event: "previous" }, ], effect: "slideLeft", }, { name: "page3", transitions: [ { target: "page4", event: "next", after: 5000 }, { target: "page2", event: "previous" }, ], effect: "scaleUp", }, { name: "page4", transitions: [ { target: "page5", event: "next", after: 5000 }, { target: "page3", event: "previous" }, ], effect: "rotate", }, { name: "page5", transitions: [ { target: "page1", event: "next", after: 5000 }, { target: "page4", event: "previous" }, ], effect: "zoom", }, ], }); engine.start(); // AnimationContent component inside the main function function AnimationContent() { const { currentStage, data, send, goTo, pauseTimers, resumeTimers, resetTimers, getTimerRemainingTime, areTimersPaused } = useStageFlow(); // Get timer information from Stage Flow const [remainingTime, setRemainingTime] = React.useState(0); const [isPaused, setIsPaused] = React.useState(false); React.useEffect(() => { const interval = setInterval(() => { if (getTimerRemainingTime) { setRemainingTime(getTimerRemainingTime()); } if (areTimersPaused) { setIsPaused(areTimersPaused()); } }, 100); return () => clearInterval(interval); }, [getTimerRemainingTime, areTimersPaused, currentStage]); // Page configurations with effects const pages = [ { id: "page1", name: "Red Page", color: "#dc3545", effect: "slideRight", }, { id: "page2", name: "Blue Page", color: "#007bff", effect: "slideLeft", }, { id: "page3", name: "Green Page", color: "#28a745", effect: "scaleUp", }, { id: "page4", name: "Purple Page", color: "#6f42c1", effect: "rotate", }, { id: "page5", name: "Orange Page", color: "#fd7e14", effect: "zoom", }, ]; const currentPage = pages.find(p => p.id === currentStage) || pages[0]; const handleNext = React.useCallback(() => { send("next"); }, [send]); const handlePrevious = React.useCallback(() => { send("previous"); }, [send]); const handlePageSelect = React.useCallback( pageId => { // Navigate directly to the selected page if (pageId !== currentStage) { goTo(pageId); } }, [currentStage, goTo] ); const handleMouseEnter = React.useCallback(() => { if (currentStage !== "loading") { pauseTimers(); } }, [currentStage, pauseTimers]); const handleMouseLeave = React.useCallback(() => { if (currentStage !== "loading") { resumeTimers(); } }, [currentStage, resumeTimers]); const handleResetTimer = React.useCallback(() => { if (currentStage !== "loading") { resetTimers(); } }, [currentStage, resetTimers]); return ( <div style={{ fontFamily: "Arial, sans-serif" }}> <style> {` @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `} </style> <div style={{ padding: "20px", maxWidth: "800px", margin: "0 auto" }}> <h2 style={{ margin: "0 0 20px 0", color: "#333", textAlign: "center" }}>Animation Effects with Stage Flow</h2> <p style={{ margin: "0 0 15px 0", color: "#666", textAlign: "center" }}> Current Stage: <strong>{currentStage}</strong> </p> {/* Timer Instructions */} <div style={{ backgroundColor: "#e3f2fd", padding: "15px", borderRadius: "8px", marginBottom: "20px", textAlign: "center", }} > <p style={{ margin: "0 0 10px 0", fontSize: "14px", color: "#1976d2" }}> <strong>Timer Control:</strong> Hover over the content area to pause timers, move mouse away to resume. </p> <p style={{ margin: "0", fontSize: "12px", color: "#666" }}> Each page auto-advances after 5 seconds. Timer shows remaining time in milliseconds. Use Reset Timer button to restart from 5 seconds. </p> </div> {/* Main Content Area */} <div style={{ position: "relative", minHeight: "400px" }}> <StageRenderer stageComponents={{ loading: LoadingStage, ...pages.reduce((acc, page) => { acc[page.id] = props => ( <PageStage {...props} page={page} remainingTime={remainingTime} isPaused={isPaused} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} onResetTimer={handleResetTimer} /> ); return acc; }, {}), }} disableAnimations={false} style={{ minHeight: "400px" }} /> </div> {/* Navigation Controls */} {currentStage !== "loading" && ( <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: "20px", padding: "20px", backgroundColor: "#f8f9fa", borderRadius: "8px", }} > <NavigationButton onClick={handlePrevious} variant="secondary"> ← Previous </NavigationButton> <div style={{ display: "flex", gap: "10px" }}> {pages.map(page => ( <PageButton key={page.id} onClick={() => handlePageSelect(page.id)} isActive={currentStage === page.id} color={page.color}> {page.name.split(" ")[0]} </PageButton> ))} </div> <NavigationButton onClick={handleNext}>Next →</NavigationButton> </div> )} </div> </div> ); } return ( <StageFlowProvider engine={engine}> <AnimationContent /> </StageFlowProvider> ); }
Result
Loading...
Code Explanation
Stage Configuration
The animation example uses multiple stages for different animation effects with automatic transitions:
const engine = new StageFlowEngine({
initial: "loading",
stages: [
{
name: "loading",
transitions: [{ target: "page1", event: "loaded" }],
effect: "fade",
},
{
name: "page1",
transitions: [
{ target: "page2", event: "next", after: 5000 },
{ target: "page5", event: "previous" },
],
effect: "slideRight",
},
// ... other stages with 5-second auto-advance
],
});
Animation Effects
Each stage demonstrates different animation effects:
- Loading: Fade effect with self-implemented 3-second timer
- Red Page: slideRight effect with 5-second auto-advance
- Blue Page: slideLeft effect with 5-second auto-advance
- Green Page: scaleUp effect with 5-second auto-advance
- Purple Page: rotate effect with 5-second auto-advance
- Orange Page: zoom effect with 5-second auto-advance
Advanced Timer Control Features
The example includes comprehensive timer control functionality that relies entirely on StageFlowEngine:
const { pauseTimers, resumeTimers, resetTimers } = useStageFlow();
// Pause timers on mouse enter
const handleMouseEnter = React.useCallback(() => {
if (currentStage !== "loading") {
pauseTimers();
}
}, [currentStage, pauseTimers]);
// Resume timers on mouse leave
const handleMouseLeave = React.useCallback(() => {
if (currentStage !== "loading") {
resumeTimers();
}
}, [currentStage, resumeTimers]);
// Reset timers to original duration
const handleResetTimer = React.useCallback(() => {
if (currentStage !== "loading") {
resetTimers();
}
}, [currentStage, resetTimers]);
Navigation System
Real-time navigation with multiple controls:
const handlePageSelect = React.useCallback(
pageId => {
if (pageId !== currentStage) {
goTo(pageId);
}
},
[currentStage, goTo]
);
Benefits
- Visual Feedback: Different animation effects for each page
- Smooth Transitions: Built-in animation system
- Flexible Navigation: Multiple ways to navigate between pages
- State Management: Centralized state for all animation data
- Component Reusability: Common UI components for consistency
- Individual Stage Components: Each page is a separate component
- Advanced Timer Control: Pause, resume, and reset functionality for automatic transitions
- Interactive Experience: Mouse hover controls for timer management
- Engine Integration: All timer functionality relies on StageFlowEngine's internal mechanisms
- Reset Button: Convenient reset functionality positioned in each stage
- Loading Stage Independence: Self-implemented timer for loading stage
Related Examples
- Setup Wizard - Multi-step configuration wizard
- Authentication Flow - Complex login/logout workflows
- Shopping Cart - Multi-step purchase process
- Basic Examples - Simple stage machine examples