Используя холст HTML/Javascript, я нарисовал 12 цифр на циферблате и теперь заставляю их вращаться по кругу, как вращающиеся вокруг планеты планеты. Код работает плавно в течение примерно 0,5 секунды, при этом console.info показывает интервал между журналами в 7 мс, но затем он замедляется, пока не регистрируется с интервалом в 30 мс всего после двух оборотов, с дальнейшей задержкой. Я также пробовал использовать setTimeout и setInterval, но не могу понять, как остановить эту задержку и получить согласованное время.
const clk = document.getElementById("clock").getContext("2d");
clk.font = "20px Arial";
clk.strokeStyle = "black";
clk.textAlign = "center";
let rotationCounter = 0; // increment rotation for each function call
function drawNumbers() {
clk.clearRect(0, 0, 400, 400); // draw the clock base
clk.fillStyle = "lightgrey";
clk.arc(200, 200, 180, 0, Math.PI * 2);
clk.fill();
clk.fillStyle = "black";
clk.translate(200, 200); // set origin to center, for drawing numbers
// draw the 12 numbers
for (let i = 1; i < 13; i++) {
let ang = (i * 30) + rotationCounter;
clk.rotate((ang * Math.PI) / 180);
clk.translate(0, -150);
clk.rotate((-ang * Math.PI) / 180)
clk.fillText(i.toString(), 0, 0);
clk.rotate((ang * Math.PI) / 180)
clk.translate(0, 150);
clk.rotate((-ang * Math.PI) / 180);
}
clk.translate(-200, -200); // return canvas to original position
rotationCounter += 1;
if (rotationCounter >= 720) {
return; // escape
}
window.requestAnimationFrame(drawNumbers);
}
window.requestAnimationFrame(drawNumbers); // initialise
<canvas id = "clock" width = "400px" height = "400px"></canvas>
Я немного подправил код, чтобы лучше контролировать скорость.
Вот в чем суть:
let lastTime = 0;
let speed = 60;
const clk = document.getElementById("clock").getContext("2d");
clk.font = "20px Arial";
clk.strokeStyle = "black";
clk.textAlign = "center";
let rotationCounter = 0;
function drawNumbers(timestamp) {
if (!lastTime) lastTime = timestamp;
let deltaTime = timestamp - lastTime; // calculate the deltatime here
lastTime = timestamp;
clk.clearRect(0, 0, 400, 400);
clk.fillStyle = "lightgrey";
clk.arc(200, 200, 180, 0, Math.PI * 2);
clk.fill();
clk.fillStyle = "black";
clk.translate(200, 200);
for (let i = 1; i < 13; i++) {
let ang = (i * 30) + rotationCounter;
clk.save(); // Store the state here
clk.rotate(ang * Math.PI / 180);
clk.translate(0, -150);
clk.rotate((-ang * Math.PI) / 180);
clk.fillText(i.toString(), 0, 0);
clk.restore(); // Reset it to previous position
}
clk.translate(-200, -200);
rotationCounter += speed * deltaTime / 1000; // this line controls the speed
if (rotationCounter >= 720) {
return;
}
window.requestAnimationFrame(drawNumbers);
}
window.requestAnimationFrame(drawNumbers);
Измените внутри цикла, как показано ниже, чтобы удалить все вращения внутри цикла. Больше не нужно сохранять холст и восстанавливать его.
for (let i = 1; i < 13; i++) {
let ang = (i * 30) + rotationCounter;
let newX = 150 * Math.cos(ang * Math.PI / 180);
let newY = 150 * Math.sin(ang * Math.PI / 180);
clk.fillText(i.toString(), newX, newY);
}
На основе комментария @Kaiido и дублированной ссылки, добавляющей BeginPath, проблема будет решена, а также предотвращено мигание круга.
clk.beginPath();
clk.clearRect(0, 0, 400, 400);
clk.fillStyle = "lightgrey";
clk.arc(200, 200, 180, 0, Math.PI * 2);
clk.fill();
clk.closePath();
Спасибо за комментарий @JaromandaX. Исправлено сейчас.
«Rotate требует большого количества ресурсов ЦП».
@Kaiido Обновил мой ответ. Наличие 4 * 12 вращений здесь требует времени.
@BardiaMazaheri еще раз, какой источник у тебя есть для подтверждения этого утверждения? Что заставляет вас думать, что настройка ротации через API (и, следовательно, использование возможных путей оптимизации) происходит медленнее, чем делать это самостоятельно? Хуже всего то, что в своей первой версии вы утверждали, что сохранение и восстановление всех атрибутов контекста происходит быстрее, чем просто изменение его CTM. Самые медленные изменения в 2D-контексте обычно связаны со стилем, а не с CTM, который обычно мало что делает, пока операции рисования не будут фактически выполнены.
но... ваш код совсем не обрабатывает числа так же, как OP