I built an analog clock using React, but I have a problem with the second hand. The second hand starts in a clockwise direction, but once it reaches the 59-second mark, it moves counter-clockwise until it reaches the 1-second mark. After that it moves clockwise again. How to make it move continuously clockwise?
import React, { useEffect, useState } from 'react'; import './AnalogClock.css'; const AnalogClock = () => { const [time, setTime] = useState(new Date()); useEffect(() => { const interval = setInterval(() => { const newTime = new Date(); setTime(newTime); }, 1000); return () => { clearInterval(interval); }; }, []); const getRotation = (unit, max) => { const value = time[`get${unit}`](); let rotation = (value * 360) / max; if (unit === 'Seconds') { const seconds = value + time.getMilliseconds() / 1000; rotation = (seconds * 6) % 360; if (rotation < 0) { rotation += 360; } } return { transform: `translate(-50%, -100%) rotate(${rotation}deg)`, }; }; const renderNumbers = () => { const numbers = []; for (let i = 1; i <= 12; i++) { const angle = (i * 30) * (Math.PI / 180); const numberStyle = { left: `calc(50% + ${Math.sin(angle) * 140}px)`, top: `calc(50% - ${Math.cos(angle) * 140}px)`, }; numbers.push( <div key={i} className="number" style={numberStyle}> {i} </div> ); } return numbers; }; return ( <div className="clock"> {renderNumbers()} <div className="hour-hand" style={getRotation('Hours', 12)}></div> <div className="minute-hand" style={getRotation('Minutes', 60)}></div> <div className="second-hand" style={ time.getSeconds() === 59 ? { ...getRotation('Seconds', 60), transition: 'none' } : getRotation('Seconds', 60) } ></div> <div className="center-circle"></div> </div> ); }; export default AnalogClock;
Not sure if the css helps, but here it is.
.clock { position: relative; width: 300px; height: 300px; border-radius: 50%; background-color: #e0e0e0; box-shadow: 20px 20px 40px rgba(0, 0, 0, 0.2), -20px -20px 40px rgba(255, 255, 255, 0.7); padding: 20px; } .hour-hand, .minute-hand, .second-hand { position: absolute; background-color: #333; transform-origin: bottom center; transition: transform 0.5s ease-in-out; box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.2), -4px -4px 8px rgba(255, 255, 255, 0.7); } .hour-hand { width: 8px; height: 90px; top: 50%; left: 50%; transform: translate(-50%, -100%) translateX(-1px); border-radius: 8px 8px 0 0; } .minute-hand { width: 6px; height: 120px; top: 50%; left: 50%; transform: translate(-50%, -100%) translateX(-1px); border-radius: 6px 6px 0 0; } .second-hand { width: 4px; height: 130px; top: 50%; left: 50%; transform: translate(-50%, -100%) translateX(-1px); border-radius: 4px 4px 0 0; background-color: #ff0000; } .center-circle { position: absolute; top: 50%; left: 50%; width: 16px; height: 16px; background-color: #333; border-radius: 50%; transform: translate(-50%, -50%); box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2), -2px -2px 4px rgba(255, 255, 255, 0.7); } .number { position: absolute; font-size: 29px; font-weight: bold; color: #333; transform: translate(-50%, -50%); }
P粉2210464252023-09-07 13:12:52
When you try to inject js in css to achieve your goals, I recommend you to use styled components to get some benefits, take it to the next level and use js variables in css
The main limitation of the code is that you cannot specify that the animation will continue forever, so that it restarts when 360 degrees is reached
This can be achieved through css keyframes and animations, but without using styled components it is difficult to pass some necessary parameters to keyframes and animations, such as the initial degree and the number of seconds to complete the loop
This is more or less how to style components
import React, { useEffect, useState } from 'react'; import styled, { keyframes } from 'styled-components'; import './styles.css'; const rotate = (degree) => keyframes` from { transform: translate(-50%, -100%) rotate(${degree}deg); } to { transform: translate(-50%, -100%) rotate(${degree+360}deg); } ` const ClockStick = styled.div` animation: ${(props) => rotate(props.degree)} ${(props) => props.seconds}s linear; ` const App = () => { const [degrees, setDegrees] = useState({}); useEffect(() => { setDegrees({ hour: getRotation('Hours', 12), minute: getRotation('Minutes', 60), second: getRotation('Seconds', 60) }) }, []); const getRotation = (unit, max) => { const time = new Date(); const value = time[`get${unit}`](); const rotation = (value * 360) / max; return rotation; }; const renderNumbers = () => { const numbers = []; for (let i = 1; i <= 12; i++) { const angle = (i * 30) * (Math.PI / 180); const numberStyle = { left: `calc(50% + ${Math.sin(angle) * 140}px)`, top: `calc(50% - ${Math.cos(angle) * 140}px)`, }; numbers.push( <div key={i} className="number" style={numberStyle}> {i} </div> ); } return numbers; }; return ( <div className="clock"> {renderNumbers()} <ClockStick className="hour-hand" seconds={216000} degree={degrees.hour}></ClockStick> <ClockStick className="minute-hand" seconds={3600} degree={degrees.minute}></ClockStick> <ClockStick className="second-hand" seconds={60} degree={degrees.second} ></ClockStick> <div className="center-circle"></div> </div> ); }; export default App;
I removed some unnecessary logic and I will explain some of the things I added
This is the result
https://codesandbox.io/s/dawn-rgb-qn58t6
Some links related to answers