반응: 아날로그 시계의 초침을 부드럽게 움직입니다.
초침 코드:
const Seconds = styled(Hours).attrs<DateProps>(({ time }) => ({
style: { transform: `rotateZ(${time.getSeconds() * 6}deg)` },
}))<DateProps>`
background-color: red;
height: 50px;
left: calc(50% - 0.5px);
top: 5px;
width: 1px;
`
첫 시도
내 즉각적인 생각은 단순히 CSS에
transition: transform 1s linear;
를 추가하는 것이 었습니다.const Seconds = styled(Hours).attrs<DateProps>(({ time }) => ({
style: { transform: `rotateZ(${time.getSeconds() * 6}deg)` },
}))<DateProps>`
background-color: red;
height: 50px;
left: calc(50% - 0.5px);
top: 5px;
transition: transform 1s linear;
width: 1px;
`
그거였다! 작업 완료...
불행히도.
처음에는 손이 59초에서 0으로 이동할 때까지 좋아 보였습니다. 계산은 어떻게 됩니까?
59 * 6 = 354
0 * 6 = 0
이것은 손이 354도에서 360도로 움직이는 것이 아니라 0도로 움직이는 것을 의미합니다. 이것은 거꾸로 달린다는 것을 의미합니다. 1초에 0으로 돌아가는 완전한 회전. 이것은 다소 재미있을 수 있지만 그것은 내가 원했던 것이 아닙니다(또는 누구나 시계에서 기대할 것입니다).
또한 여기에는 또 다른 작은 문제가 있습니다. 손이 1초 뒤에 있습니다. 생각해보세요:
한 위치에서 다른 위치로 점프할 때 손은 즉시 목표 위치(현재 초의 위치)에 있습니다. 1초 이내에 전환하면 이전 초에서 시작하여 현재 위치에 도달하는 데 1초가 걸립니다. 어쨌든 이것은
time.getSeconds()
에 1을 추가하여 쉽게 해결할 수 있습니다.하지만 우리는 더 잘할 수 있습니다.
솔루션에 대한 생각
마음에 떠오른 첫 번째 솔루션으로 점프한 후 이를 수행할 수 있는 다른 가능한 방법에 대해 생각하기 시작했습니다.
time.getSeconds()
를 사용하지 않고 Date.now()
만 사용하면 어떻게 됩니까? 그것은 1970년 1월 1일 이후의 밀리초를 얻을 것입니다. 그것을 1000으로 나누고 그것을 사용하는 것이 트릭을 할 것입니다.불행히도 그렇게 쉬운 일이 아닙니다. 생성된 div는 다음과 같습니다.
<div class="sc-cxabCf bIucpB" style="transform: rotateZ(9.96329e+09deg);"></div>
숫자가 너무 컸습니다. 또한 시작하기 위해 9963290976도와 같은 것을 사용하는 것이 좋은 습관인지 확실하지 않습니다.
내가 생각한 다른 솔루션은 다음과 같습니다.
Date.now() - start
). 이 솔루션은 합리적인 것 같아서 계속 시도했습니다. 시간 델타 사용
시작 시간과 지금 사이의 차이를 계산하려면 시작 지점을 어딘가에 저장해야 했습니다. 이것은 전혀 변경되어서는 안되며 재렌더링을 다시 트리거하지 않아야 하므로 이를 위해 ref를 사용하고 있습니다.
const DemoClock: React.FC = () => {
const [time, setTime] = useState(() => new Date())
const start = useRef(time)
useEffect(() => {
const interval = setInterval(() => {
const now = new Date()
if (time.getSeconds() !== now.getSeconds()) {
setTime(now)
}
}, 250)
return () => clearInterval(interval)
}, [time])
const seconds = Math.floor((time.getTime() - start.current.getTime()) / 1000)
+ start.current.getSeconds() + 1
return (
<Clock>
<Hours time={time} />
<Minutes time={time} />
<Seconds seconds={seconds} />
</Clock>
)
}
이것은 잘 작동합니다. 하지만 한 가지는 여전히 저를 괴롭혔습니다. 학위는 점점 더 커지고 있습니다. 이것이 문제라고 생각하지 않지만 이러한 큰 숫자가 필요하지 않은 솔루션을 시도하고 싶었습니다.
웹 애니메이션 API 사용
나는 이것을 끝내었다 :
const DemoClock: React.FC = () => {
const [time, setTime] = useState(() => new Date())
const secondsRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const interval = setInterval(() => {
const now = new Date()
if (time.getSeconds() !== now.getSeconds()) {
setTime(now)
}
}, 250)
return () => clearInterval(interval)
}, [time])
useLayoutEffect(() => {
if (!secondsRef.current) {
return
}
secondsRef.current.animate(
[
{
transform: `rotateZ(${(time.getSeconds() * 6}deg)`,
},
{
transform: `rotateZ(${(time.getSeconds() + 1 * 6}deg)`,
},
],
{
duration: 1000,
fill: 'both',
iterations: 1,
}
)
}, [time])
return (
<Clock>
<Hours time={time} />
<Minutes time={time} />
<Seconds time={time} ref={secondsRef} />
</Clock>
)
}
각 단계의 시작과 끝을 정의함으로써 알 수 있듯이 우리는 결코 뒤로 물러서야 하는 상황에 빠지지 않습니다. 또한 손을 미치광이처럼 돌리지 않고 언제든지 시간을 변경할 수 있으며 360도보다 큰 각도를 사용할 필요가 없습니다. 저에게는 이것이 이상적인 솔루션처럼 느껴졌습니다.
당신의 해결책은 무엇입니까? 나는 명백한 것을 놓쳤습니까? 알려줘요.
사진 제공: Ocean Ng on Unsplash
Reference
이 문제에 관하여(반응: 아날로그 시계의 초침을 부드럽게 움직입니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/syeo66/react-smoothly-move-the-seconds-hand-of-the-analog-click-hji텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)