How to make the second hand on a clock only move forward?
P粉952365143
P粉952365143 2023-09-06 15:56:38
0
1
607

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粉952365143
P粉952365143

reply all(1)
P粉221046425

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

  • Keyframes are essential to achieve this goal as they will define how the animation rotates (from initial rotation to initial rotation 360) and can also be used with animation properties, which can be helpful with ClockStick
  • ClockStick is a style component that will receive the initial degree and the number of seconds it takes for the animation to complete a loop
  • I only declared a state whose initial degree corresponds to the specific time the user ran the application
  • I then pass the number of seconds it takes for each stick (ClockStick) to complete the loop (216000 => for hours, 3600 => for minutes, 60 => for seconds)

This is the result

https://codesandbox.io/s/dawn-rgb-qn58t6

Some links related to answers

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template