light cycle

This commit is contained in:
2025-10-25 16:13:58 +08:00
parent 21bd827520
commit b09f08849f
5 changed files with 817 additions and 8 deletions

View File

@@ -0,0 +1,151 @@
import { Box, Image, Button, HStack, VStack, Text } from '@chakra-ui/react'
import { useState, useEffect, CSSProperties } from 'react'
interface CyclingImageProps {
src: string
position?: CSSProperties['position']
w?: any
left?: any
top?: any
right?: any
bottom?: any
cycleDuration?: number // Duration of one complete cycle in seconds
intensity?: number // How dark/light it gets (0-1, where 1 is maximum)
timingFunction?: string // CSS timing function
showControls?: boolean // Show play/pause and speed controls
autoStart?: boolean // Auto-start animation
style?: CSSProperties // Additional CSS styles
}
const CyclingImage = ({
src,
position = 'relative',
w,
left,
top,
right,
bottom,
cycleDuration = 3,
intensity = 0.5,
timingFunction = 'ease-in-out',
showControls = false,
autoStart = true,
style = {}
}: CyclingImageProps) => {
const [isPlaying, setIsPlaying] = useState(autoStart)
const [speed, setSpeed] = useState(cycleDuration)
const [currentIntensity, setCurrentIntensity] = useState(intensity)
useEffect(() => {
setSpeed(cycleDuration)
}, [cycleDuration])
const togglePlayPause = () => {
setIsPlaying(!isPlaying)
}
const handleSpeedChange = (value: number) => {
setSpeed(value)
}
const handleIntensityChange = (value: number) => {
setCurrentIntensity(value)
}
// CSS keyframes animation
const animationName = 'lightToDarkCycle'
const animationStyle = `
@keyframes ${animationName} {
0% {
filter: brightness(${1 + currentIntensity});
}
50% {
filter: brightness(${1 - currentIntensity});
}
100% {
filter: brightness(${1 + currentIntensity}) ;
}
}
`
return (
<>
<style>{animationStyle}</style>
<Image
src={src}
position={position}
w={w}
left={left}
top={top}
right={right}
bottom={bottom}
style={{
...style,
animation: isPlaying ? `${animationName} ${speed}s ${timingFunction} infinite` : 'none',
willChange: 'filter',
transition: 'filter 0.3s ease'
}}
/>
{showControls && (
<Box
position="absolute"
bottom={typeof bottom === 'object' ? '-120px' : `calc(${bottom} - 120px)`}
left={left}
zIndex={10}
bg="rgba(0, 0, 0, 0.7)"
borderRadius="md"
p={3}
minW="250px"
>
<VStack gap={3} align="stretch">
<HStack justify="space-between">
<Text color="white" fontSize="sm" fontWeight="bold">
Animation Controls
</Text>
<Button
size="sm"
onClick={togglePlayPause}
colorScheme={isPlaying ? 'red' : 'green'}
>
{isPlaying ? 'Pause' : 'Play'}
</Button>
</HStack>
<Box>
<Text color="white" fontSize="xs" mb={1}>
Speed: {speed.toFixed(1)}s
</Text>
<input
type="range"
value={speed}
onChange={(e) => handleSpeedChange(parseFloat(e.target.value))}
min={0.5}
max={10}
step={0.5}
style={{ width: '100%', cursor: 'pointer' }}
/>
</Box>
<Box>
<Text color="white" fontSize="xs" mb={1}>
Intensity: {(currentIntensity * 100).toFixed(0)}%
</Text>
<input
type="range"
value={currentIntensity}
onChange={(e) => handleIntensityChange(parseFloat(e.target.value))}
min={0}
max={1}
step={0.1}
style={{ width: '100%', cursor: 'pointer' }}
/>
</Box>
</VStack>
</Box>
)}
</>
)
}
export default CyclingImage

View File

@@ -0,0 +1,219 @@
import { Box, Heading, VStack, Text, SimpleGrid } from '@chakra-ui/react'
import CyclingImage from './CyclingImage'
/**
* Demo component showcasing various configurations of CyclingImage
* This demonstrates the different ways to use the cycling animation effect
*/
function CyclingImageDemo() {
return (
<Box p={8} bg="gray.50" minH="100vh">
<VStack gap={8} align="stretch">
<Box textAlign="center">
<Heading size="2xl" mb={2}>CyclingImage Component Demo</Heading>
<Text color="gray.600">
Showcasing continuous light-to-dark color cycling animations
</Text>
</Box>
<SimpleGrid columns={{ base: 1, md: 2 }} gap={8}>
{/* Example 1: Default Settings */}
<Box bg="white" p={6} borderRadius="lg" shadow="md">
<Heading size="md" mb={4}>Default Settings</Heading>
<Text fontSize="sm" color="gray.600" mb={4}>
3s cycle, 50% intensity, auto-start
</Text>
<Box position="relative" h="300px" bg="gray.100" borderRadius="md">
<CyclingImage
src="/images/new/threehightext.webp"
position="absolute"
w="200px"
left="50%"
top="50%"
style={{ transform: 'translate(-50%, -50%)' } as any}
/>
</Box>
</Box>
{/* Example 2: With Controls */}
<Box bg="white" p={6} borderRadius="lg" shadow="md">
<Heading size="md" mb={4}>With Interactive Controls</Heading>
<Text fontSize="sm" color="gray.600" mb={4}>
Adjustable speed and intensity
</Text>
<Box position="relative" h="300px" bg="gray.100" borderRadius="md">
<CyclingImage
src="/images/new/fattext.webp"
position="absolute"
w="200px"
left="50%"
top="50%"
style={{ transform: 'translate(-50%, -50%)' } as any}
showControls={true}
cycleDuration={4}
intensity={0.7}
/>
</Box>
</Box>
{/* Example 3: Fast Cycle */}
<Box bg="white" p={6} borderRadius="lg" shadow="md">
<Heading size="md" mb={4}>Fast Cycle (1s)</Heading>
<Text fontSize="sm" color="gray.600" mb={4}>
Quick pulsing effect with high intensity
</Text>
<Box position="relative" h="300px" bg="gray.100" borderRadius="md">
<CyclingImage
src="/images/new/centerfattext.webp"
position="absolute"
w="200px"
left="50%"
top="50%"
style={{ transform: 'translate(-50%, -50%)' } as any}
cycleDuration={1}
intensity={0.8}
/>
</Box>
</Box>
{/* Example 4: Slow & Subtle */}
<Box bg="white" p={6} borderRadius="lg" shadow="md">
<Heading size="md" mb={4}>Slow & Subtle (8s)</Heading>
<Text fontSize="sm" color="gray.600" mb={4}>
Gentle breathing effect with low intensity
</Text>
<Box position="relative" h="300px" bg="gray.100" borderRadius="md">
<CyclingImage
src="/images/new/hairlosstext.webp"
position="absolute"
w="200px"
left="50%"
top="50%"
style={{ transform: 'translate(-50%, -50%)' } as any}
cycleDuration={8}
intensity={0.3}
timingFunction="linear"
/>
</Box>
</Box>
{/* Example 5: Paused by Default */}
<Box bg="white" p={6} borderRadius="lg" shadow="md">
<Heading size="md" mb={4}>Paused by Default</Heading>
<Text fontSize="sm" color="gray.600" mb={4}>
Animation starts paused, with controls
</Text>
<Box position="relative" h="300px" bg="gray.100" borderRadius="md">
<CyclingImage
src="/images/new/threehightext.webp"
position="absolute"
w="200px"
left="50%"
top="50%"
style={{ transform: 'translate(-50%, -50%)' } as any}
showControls={true}
autoStart={false}
cycleDuration={3}
intensity={0.6}
/>
</Box>
</Box>
{/* Example 6: Custom Timing Function */}
<Box bg="white" p={6} borderRadius="lg" shadow="md">
<Heading size="md" mb={4}>Ease-In-Out-Back</Heading>
<Text fontSize="sm" color="gray.600" mb={4}>
Custom cubic-bezier timing function
</Text>
<Box position="relative" h="300px" bg="gray.100" borderRadius="md">
<CyclingImage
src="/images/new/fattext.webp"
position="absolute"
w="200px"
left="50%"
top="50%"
style={{ transform: 'translate(-50%, -50%)' } as any}
cycleDuration={5}
intensity={0.7}
timingFunction="cubic-bezier(0.68, -0.55, 0.265, 1.55)"
/>
</Box>
</Box>
</SimpleGrid>
{/* Features List */}
<Box bg="white" p={6} borderRadius="lg" shadow="md">
<Heading size="md" mb={4}>Component Features</Heading>
<VStack align="start" gap={2}>
<Text> Smooth continuous light-to-dark cycling animation</Text>
<Text> Fully responsive with all Chakra UI responsive props</Text>
<Text> Maintains absolute/relative positioning</Text>
<Text> Customizable cycle duration (0.5s - 10s+)</Text>
<Text> Adjustable intensity (0-100%)</Text>
<Text> Multiple timing functions (ease, linear, cubic-bezier)</Text>
<Text> Play/Pause controls (optional)</Text>
<Text> Real-time speed adjustment</Text>
<Text> Real-time intensity adjustment</Text>
<Text> Auto-start or paused initial state</Text>
<Text> CSS-based animations (hardware accelerated)</Text>
<Text> Minimal performance impact</Text>
</VStack>
</Box>
{/* Usage Examples */}
<Box bg="white" p={6} borderRadius="lg" shadow="md">
<Heading size="md" mb={4}>Usage Examples</Heading>
<VStack align="start" gap={4}>
<Box>
<Text fontWeight="bold" mb={2}>Basic Usage:</Text>
<Box as="pre" bg="gray.100" p={3} borderRadius="md" fontSize="sm" overflowX="auto">
{`<CyclingImage
src="/path/to/image.png"
position="absolute"
w="200px"
left="50px"
top="100px"
/>`}
</Box>
</Box>
<Box>
<Text fontWeight="bold" mb={2}>With Custom Settings:</Text>
<Box as="pre" bg="gray.100" p={3} borderRadius="md" fontSize="sm" overflowX="auto">
{`<CyclingImage
src="/path/to/image.png"
position="absolute"
w={{ base: "100px", lg: "200px" }}
left={{ base: "20px", lg: "50px" }}
top="100px"
cycleDuration={5}
intensity={0.7}
timingFunction="ease-in-out"
/>`}
</Box>
</Box>
<Box>
<Text fontWeight="bold" mb={2}>With Interactive Controls:</Text>
<Box as="pre" bg="gray.100" p={3} borderRadius="md" fontSize="sm" overflowX="auto">
{`<CyclingImage
src="/path/to/image.png"
position="absolute"
w="200px"
left="50px"
top="100px"
showControls={true}
autoStart={false}
cycleDuration={3}
intensity={0.6}
/>`}
</Box>
</Box>
</VStack>
</Box>
</VStack>
</Box>
)
}
export default CyclingImageDemo

View File

@@ -1,4 +1,5 @@
import { Box, Image, Stack, } from '@chakra-ui/react'
import CyclingImage from './CyclingImage'
function Hero1() {
const bigWarningSize = { base: "80px", sm: "100px", md: "120px", lg: "9vw", xl: "8vw" };
@@ -32,54 +33,78 @@ function Hero1() {
maxW={{ base: "40px", sm: "50px", md: "60px", lg: "70%", xl: "70%" }}
bottom={'10px'} />
{/* signs */}
<Image src="/images/new/bigwarning.webp"
<CyclingImage
src="/images/new/bigwarning.webp"
position={'absolute'}
w={bigWarningSize}
left={'20vw'}
top={'5vw'}
cycleDuration={3}
intensity={0.1}
/>
<Image src="/images/new/bigwarning.webp"
<CyclingImage
src="/images/new/bigwarning.webp"
position={'absolute'}
w={smallWarningSize}
left={'25vw'}
bottom={'18vw'}
cycleDuration={3}
intensity={0.1}
/>
<Image src="/images/new/bigwarning.webp"
<CyclingImage
src="/images/new/bigwarning.webp"
position={'absolute'}
w={bigWarningSize}
right={'20vw'}
top={'25vw'}
cycleDuration={3}
intensity={0.1}
/>
<Image src="/images/new/bigwarning.webp"
<CyclingImage
src="/images/new/bigwarning.webp"
position={'absolute'}
w={bigWarningSize}
right={'18vw'}
top={'10vw'}
cycleDuration={3}
intensity={0.1}
/>
{/* Warning Texts */}
<Image src="/images/new/threehightext.webp"
<CyclingImage
src="/images/new/threehightext.webp"
position={'absolute'}
w={{ base: "100px", sm: "120px", md: "150px", lg: "11vw", xl: "10vw" }}
left={{ base: "20px", sm: "30px", md: "40px", lg: "30vw", xl: "37vw" }}
top={{ base: "3vw", sm: "3vw", md: "3vw", lg: "7vw", xl: "3vw" }}
cycleDuration={3}
intensity={0.1}
/>
<Image src="/images/new/fattext.webp"
<CyclingImage
src="/images/new/fattext.webp"
position={'absolute'}
w={{ base: "100px", sm: "120px", md: "150px", lg: "10vw", xl: "9vw" }}
left={{ base: "25vw", sm: "25vw", md: "25vw", lg: "18vw", xl: "25vw" }}
top={{ base: "14vw", sm: "14vw", md: "14vw", lg: "20vw", xl: "14vw" }}
cycleDuration={3}
intensity={0.1}
/>
<Image src="/images/new/centerfattext.webp"
<CyclingImage
src="/images/new/centerfattext.webp"
position={'absolute'}
w={{ base: "100px", sm: "120px", md: "150px", lg: "14vw", xl: "13vw" }}
right={{ base: "35vw", sm: "35vw", md: "35vw", lg: "34vw", xl: "35vw" }}
top={{ base: "5vw", sm: "5vw", md: "5vw", lg: "7vw", xl: "5vw" }}
cycleDuration={3}
intensity={0.1}
/>
<Image src="/images/new/hairlosstext.webp"
<CyclingImage
src="/images/new/hairlosstext.webp"
position={'absolute'}
w={{ base: "100px", sm: "120px", md: "150px", lg: "8vw", xl: "7vw" }}
right={{ base: "26vw", sm: "26vw", md: "26vw", lg: "26vw", xl: "26vw" }}
top={{ base: "12vw", sm: "12vw", md: "12vw", lg: "18vw", xl: "12vw" }}
cycleDuration={3}
intensity={0.1}
/>
</Box>
</Stack>