1 回答
TA貢獻1824條經驗 獲得超5個贊
這里有一個基本的設計問題,那就是設置間隔是保持時間的錯誤工具。 只保證回調不會運行至少1000毫秒,而不是保證它將在1000毫秒內完全運行,所以你最終會得到漂移和跳過的時間。setInterval
通過此設置,您可以使用以下命令將時針與小時剩余的分鐘數成比例地縮放:
(hours + minutes / 60) * 30 + 180
如果您希望對時針調整進行更粗糙的粒度,請將分鐘截斷為 6 個不同的塊:
(hours + floor(minutes / 10) * 10 / 60) * 30 + 180
在數學上執行此操作比在硬編碼數組中查找增量點要混亂得多。
下面是一個最小的示例,可用于保持準確的時間(我將樣式留給您):
.hand {
width: 2px;
height: 40%;
background-color: black;
transform-origin: top center;
position: absolute;
border-radius: 3px;
top: 50%;
left: 50%;
}
.analog-clock {
position: relative;
border-radius: 50%;
border: 1px solid #aaa;
height: 120px;
width: 120px;
}
<script type="text/babel" defer>
const {Fragment, useEffect, useState, useRef} = React;
const Clock = () => {
const [date, setDate] = useState(new Date());
const requestRef = useRef();
let prevDate = null;
const tick = () => {
const now = new Date();
if (prevDate && now.getSeconds() !== prevDate.getSeconds()) {
setDate(now);
}
prevDate = now;
requestRef.current = requestAnimationFrame(tick);
};
useEffect(() => {
requestRef.current = requestAnimationFrame(tick);
return () => cancelAnimationFrame(requestRef.current);
}, []);
const pad = n => n.toString().padStart(2, 0);
const computeHourDeg = date =>
(date.getHours() + ~~(date.getMinutes() / 10) * 10 / 60) * 30 + 180
;
return (
<Fragment>
<div className="analog-clock">
<div
className="hand"
style={{transform: `rotate(${6 * date.getSeconds() + 180}deg)`}}
></div>
<div
className="hand"
style={{transform: `rotate(${6 * date.getMinutes() + 180}deg)`}}
></div>
<div
className="hand"
style={{background: "red",
height: "30%",
transform: `rotate(${computeHourDeg(date)}deg)`}}
></div>
</div>
<h3>
{pad(date.getHours())}:
{pad(date.getMinutes())}:
{pad(date.getSeconds())}
</h3>
</Fragment>
);
};
ReactDOM.render(<Clock />, document.body);
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
下面是一個帶有模擬對象的加速版本,用于說明它工作正常:Date
.hand {
width: 2px;
height: 40%;
background-color: black;
transform-origin: top center;
position: absolute;
border-radius: 3px;
top: 50%;
left: 50%;
}
.analog-clock {
position: relative;
border-radius: 50%;
border: 1px solid #aaa;
height: 120px;
width: 120px;
}
<script type="text/babel" defer>
const {Fragment, useEffect, useState, useRef} = React;
const speedMS = 5;
class MockDate {
static second = 0;
static minute = 0;
static hour = 0;
constructor() {
this.second = MockDate.second;
this.minute = MockDate.minute;
this.hour = MockDate.hour;
}
getSeconds() {
return this.second;
}
getMinutes() {
return this.minute;
}
getHours() {
return this.hour || 12;
}
}
setInterval(() => {
if (++MockDate.second === 60) {
MockDate.second = 0;
if (++MockDate.minute === 60) {
MockDate.minute = 0;
MockDate.hour = (MockDate.hour + 1) % 12;
}
}
}, speedMS);
const Clock = () => {
const [date, setDate] = useState(new MockDate());
const requestRef = useRef();
let prevDate = null;
const tick = () => {
const now = new MockDate();
if (prevDate && now.getSeconds() !== prevDate.getSeconds()) {
setDate(now);
}
prevDate = now;
requestRef.current = requestAnimationFrame(tick);
};
useEffect(() => {
requestRef.current = requestAnimationFrame(tick);
return () => cancelAnimationFrame(requestRef.current);
}, []);
const pad = n => n.toString().padStart(2, 0);
const computeHourDeg = date =>
(date.getHours() + ~~(date.getMinutes() / 10) * 10 / 60) * 30 + 180
;
return (
<Fragment>
<div className="analog-clock">
<div
className="hand"
style={{transform: `rotate(${6 * date.getSeconds() + 180}deg)`}}
></div>
<div
className="hand"
style={{transform: `rotate(${6 * date.getMinutes() + 180}deg)`}}
></div>
<div
className="hand"
style={{background: "red",
height: "30%",
transform: `rotate(${computeHourDeg(date)}deg)`}}
></div>
</div>
<h3>
{pad(date.getHours())}:
{pad(date.getMinutes())}:
{pad(date.getSeconds())}
</h3>
</Fragment>
);
};
ReactDOM.render(<Clock />, document.body);
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
添加回答
舉報
