搜索

首页  >  问答  >  正文

如何让时钟上的秒针只向前走动?

我使用 React 构建了一个模拟时钟,但秒针有问题。 秒针以顺时针方向开始,但一旦到达 59 秒标记,它就会逆时针移动,直到到达 1 秒标记。之后它再次顺时针移动。如何让它连续顺时针移动?

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;

不确定 css 是否有帮助,但它就在这里。

.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粉952365143P粉952365143435 天前588

全部回复(1)我来回复

  • P粉221046425

    P粉2210464252023-09-07 13:12:52

    当你尝试在 css 中注入 js 来实现你的目标时,我建议你使用样式化组件来获得一些好处,将其提升到一个新的水平,并在 css 中使用 js 变量

    代码的主要限制是您无法指定动画将永远继续,以便在达到 360 度时重新启动动画

    这可以通过 css 关键帧和动画来实现,但如果不使用样式化组件,则很难将一些必要的参数传递给关键帧和动画,例如初始度数和完成循环的秒数

    这或多或少是样式化组件的方法

    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;
    

    我删除了一些不必要的逻辑,我将解释我添加的一些内容

    • 关键帧对于实现此目标至关重要,因为它将定义动画如何旋转(从初始旋转到初始旋转 + 360),并且还可以与动画属性一起使用,这对 ClockStick 很有帮助
    • ClockStick 是一个样式组件,它将接收初始度数以及动画完成一个循环所需的秒数
    • 我仅声明了一种状态,其初始度数与用户运行应用程序的特定时间相对应
    • 然后,我传递每根棒 (ClockStick) 完成循环所需的秒数(216000 => 表示小时,3600 => 表示分钟,60 => 表示秒)

    这是结果

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

    与答案相关的一些链接

    回复
    0
  • 取消回复