setInterval() с интервалом в 1 миллисекунду
Я пишу кликер на JS и столкнулся с тем, что если я с помощью setInterval()
увеличиваю количество денег на 0,001 раз в миллисекунду (то есть справедливо сказать, что увеличиваю на 1 в секунду), то невооружённым глазом видно, что прирост значительно меньше, чем +1 в секунду
Смысл происходящего в том, чтобы обновлять раз в миллисекунду кол-во денег (чтобы был эффект бегущих циферок), а пользователю показывать сколько он получает денег в секунду с автокликов:
<body>
<h1>Money: <span id="money"></span></h1>
<p><span id="per_sec"></span>/s</p>
<button id="autoclick" onclick="increaseAutoclick()">+1/s</button>
<script>
const stats = {
money: 0,
autoclickValue: 0,
};
const moneyTag = document.querySelector("#money");
const perSecondTag = document.querySelector("#per_sec");
moneyTag.innerText = stats.money;
perSecondTag.innerText = stats.autoclickValue;
В
const increaseAutoclick = () => {
stats.autoclickValue += 1;
perSecondTag.innerText = stats.autoclickValue;
};
// увеличиваем количество денег на 0,001 раз в миллисекунду
setInterval(() => {
stats.money += stats.autoclickValue * 0.001;
moneyTag.innerText = stats.money.toFixed(2);
}, 1);
</script>
</body>
Методом научного тыка я обнаружил, что прирост становится ровно +1 в секунду, если добавлять раз в миллисекунду не 0.001
, а 0.004
:
setInterval(() => {
stats.money += stats.autoclickValue * 0.004;
moneyTag.innerText = stats.money.toFixed(2);
}, 1);
В чём причина? В точности setInterval()
, её асинхронности или может аппроксимации десятичных дробей в JS?
И возможно есть лучшие способ реализовать такое, не через setInterval()
?
Хочется чётко понимать такие базовые отношения с числами, заранее спасибо!
Ответы (1 шт):
Используйте requestAnimationFrame для анимаций. Он вызывается с той скоростью, с которой браузер способен обновлять экран. Обновлять значения чаще нет смысла. Интервал вызова может оказаться неравномерным, надо написать функцию которая будет менять экран в зависимости от прошедшего времени:
const counter = document.getElementById('counter').firstChild;
const start = performance.now();
const frame = () => {
const dt = (performance.now() - start) / 1000;
counter.nodeValue = dt.toFixed(2);
requestAnimationFrame(frame);
};
requestAnimationFrame(frame);
<span id="counter">0</span>