Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import { useState, useEffect, useRef } from "react";
- export default function AnalogClock() {
- // Thème par défaut
- const prefersDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches;
- const [isDarkMode, setIsDarkMode] = useState(prefersDarkMode);
- // Date courante mise à jour chaque seconde
- const [time, setTime] = useState(() => new Date());
- // Angle de la trotteuse, géré manuellement
- const [secondsAngle, setSecondsAngle] = useState(6 * time.getSeconds());
- // Pour activer/désactiver la transition CSS de la trotteuse
- const [skipTransition, setSkipTransition] = useState(false);
- // On garde l’ancien angle pour détecter le saut 59s → 0s
- const oldAngleRef = useRef(secondsAngle);
- useEffect(() => {
- // Mise à jour chaque seconde
- const interval = setInterval(() => {
- setTime(new Date());
- }, 1000);
- return () => clearInterval(interval);
- }, []);
- useEffect(() => {
- const newSeconds = time.getSeconds();
- const newAngle = 6 * newSeconds; // 6° par seconde (0 → 354)
- const oldAngle = oldAngleRef.current;
- // --- Gestion du passage 59 → 0 pour éviter la rotation inverse ---
- if (newAngle < oldAngle - 300) {
- // 1) On autorise la transition pour terminer le mouvement 354° → 360° en 500ms
- setSkipTransition(false);
- setSecondsAngle(360);
- // 2) Après 500ms, on passe instantanément à 0° sans transition
- setTimeout(() => {
- setSkipTransition(true);
- setSecondsAngle(0);
- }, 500);
- } else {
- // --- Cas normal : l’aiguille s’anime sur 500ms, puis reste figée 500ms ---
- // Phase d’animation (0 → 500ms)
- setSkipTransition(false);
- setSecondsAngle(newAngle);
- // Phase sans transition (500 → 1000ms)
- setTimeout(() => {
- setSkipTransition(true);
- }, 500);
- }
- oldAngleRef.current = newAngle;
- }, [time]);
- // --- Aiguilles heures + minutes ---
- const rawHours = time.getHours() % 12;
- const rawMinutes = time.getMinutes();
- const rawSeconds = time.getSeconds();
- // Mouvement fluide (optionnel) :
- const minutePrecise = rawMinutes + rawSeconds / 60;
- const hourPrecise = rawHours + minutePrecise / 60;
- const hoursRotation = 30 * hourPrecise;
- const minutesRotation = 6 * minutePrecise;
- // Format 24h
- const formattedTime = time.toLocaleTimeString("fr-FR", { hour12: false });
- const transitionClass = "transition-colors duration-500 ease-in-out";
- return (
- <div
- className={`flex flex-col justify-center items-center h-screen ${transitionClass} ${
- isDarkMode ? "bg-gray-900 text-white" : "bg-gray-200 text-black"
- }`}
- >
- {/* Cadran */}
- <div
- className={`relative w-64 h-64 rounded-full border-none flex justify-center items-center transition-all duration-500 ${
- isDarkMode
- ? "bg-gray-800 shadow-[inset_-8px_-8px_15px_rgba(255,255,255,0.15),inset_8px_8px_15px_rgba(0,0,0,0.7)]"
- : "bg-gray-100 shadow-[inset_-8px_-8px_15px_rgba(255,255,255,0.7),inset_8px_8px_15px_rgba(0,0,0,0.2)]"
- }`}
- >
- {/* Marquages des heures */}
- {Array.from({ length: 12 }).map((_, i) => (
- <div
- key={i}
- className={`absolute w-1 h-6 ${transitionClass} ${
- isDarkMode ? "bg-gray-300" : "bg-black"
- }`}
- style={{ transform: `rotate(${i * 30}deg) translateY(-110px)` }}
- />
- ))}
- {/* Marquages des minutes */}
- {Array.from({ length: 60 }).map((_, i) => (
- <div
- key={i}
- className={`absolute w-0.5 h-2 ${transitionClass} ${
- isDarkMode ? "bg-gray-500" : "bg-gray-400"
- }`}
- style={{
- transform: `rotate(${i * 6}deg) translateY(-110px)`,
- opacity: i % 5 === 0 ? 0 : 1,
- }}
- />
- ))}
- {/* Chiffres */}
- {Array.from({ length: 12 }).map((_, i) => (
- <div
- key={i}
- className={`absolute text-xl font-bold ${transitionClass} ${
- isDarkMode ? "text-gray-300" : "text-black"
- }`}
- style={{
- transform: `rotate(${i * 30}deg) translateY(-85px) rotate(-${i * 30}deg)`,
- }}
- >
- {i === 0 ? 12 : i}
- </div>
- ))}
- {/* Aiguille des heures (fondu sur la couleur + rotation) */}
- <div
- className={`
- absolute
- w-1.5
- rounded-full
- opacity-75
- ease-in-out
- ${isDarkMode ? "bg-gray-300" : "bg-black"}
- `}
- style={{
- height: "30%",
- top: "20%",
- left: "50%",
- transformOrigin: "bottom",
- transform: `translateX(-50%) rotate(${hoursRotation}deg)`,
- transition: "transform 0.5s ease-in-out, background-color 0.5s ease-in-out",
- }}
- />
- {/* Aiguille des minutes (fondu sur la couleur + rotation) */}
- <div
- className={`
- absolute
- w-1.5
- rounded-full
- opacity-75
- ease-in-out
- ${isDarkMode ? "bg-gray-300" : "bg-black"}
- `}
- style={{
- height: "45%",
- top: "5%",
- left: "50%",
- transformOrigin: "bottom",
- transform: `translateX(-50%) rotate(${minutesRotation}deg)`,
- transition: "transform 0.5s ease-in-out, background-color 0.5s ease-in-out",
- }}
- />
- {/* Aiguille des secondes (anime 0→500ms, figée 500→1000ms) */}
- <div
- className={`
- absolute
- w-0.5
- rounded-full
- shadow-[4px_4px_10px_rgba(255,165,0,0.5),-4px_-4px_10px_rgba(255,69,0,0.5)]
- opacity-75
- ${isDarkMode ? "bg-orange-400" : "bg-orange-500"}
- `}
- style={{
- height: "50%",
- top: "0%",
- left: "50%",
- transformOrigin: "bottom",
- transform: `translateX(-50%) rotate(${secondsAngle}deg)`,
- transition: skipTransition
- ? "none"
- : "transform 0.5s ease-in-out, background-color 0.5s ease-in-out",
- }}
- />
- {/* Centre */}
- <div
- className={`
- absolute
- w-5
- h-5
- border-2
- rounded-full
- shadow-inner
- opacity-100
- ease-in-out
- ${isDarkMode ? "bg-orange-500 border-gray-300" : "bg-orange-500 border-black"}
- `}
- style={{
- transition: "all 0.5s ease-in-out",
- }}
- />
- </div>
- {/* Horloge numérique */}
- <div className="mt-6 text-2xl font-mono font-semibold">{formattedTime}</div>
- {/* Bouton Jour/Nuit */}
- <button
- className={`
- mt-8
- px-4
- py-2
- bg-gray-700
- text-white
- rounded-lg
- shadow-md
- hover:bg-gray-600
- ${transitionClass}
- `}
- onClick={() => setIsDarkMode(!isDarkMode)}
- >
- {isDarkMode ? "🌞 Mode Jour" : "🌙 Mode Nuit"}
- </button>
- </div>
- );
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement