HTML и Javascript: анимация орбиты номера часов запаздывает через 1 секунду

Используя холст 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>
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я немного подправил код, чтобы лучше контролировать скорость.

Вот в чем суть:

  1. Вращение здесь является потребителем времени, и использование поворота 4 * 12 и перемещения 2 * 12 для возврата холста в его положение может сделать процесс дольше, чем временной интервал. Таким образом, сохранение состояния холста перед вращением и восстановлением может уменьшить количество вращений на 2 * 12 и перемещение на 1 * 12. (См. обновленный ответ, в котором удалены все повороты и переводы)
  2. частота кадров может меняться в зависимости от производительности системы. Если вам нужна постоянная скорость в любых состояниях, вам понадобится deltaTime между таймфреймами. Таким образом, в ситуациях с низкой производительностью (например, высокая загрузка процессора) частота кадров уменьшается, deltaTime увеличивается, и ваш счетчик вращения должен быть больше. В ситуациях с высокой производительностью deltaTime меньше, а RotationCounter должен быть меньше.
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);
            

Обновление 1

Измените внутри цикла, как показано ниже, чтобы удалить все вращения внутри цикла. Больше не нужно сохранять холст и восстанавливать его.

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);        
}

Обновление 2

На основе комментария @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();

но... ваш код совсем не обрабатывает числа так же, как OP

Jaromanda X 16.08.2024 03:28

Спасибо за комментарий @JaromandaX. Исправлено сейчас.

Bardia Mazaheri 16.08.2024 03:48

«Rotate требует большого количества ресурсов ЦП».

Kaiido 16.08.2024 04:00

@Kaiido Обновил мой ответ. Наличие 4 * 12 вращений здесь требует времени.

Bardia Mazaheri 16.08.2024 04:10

@BardiaMazaheri еще раз, какой источник у тебя есть для подтверждения этого утверждения? Что заставляет вас думать, что настройка ротации через API (и, следовательно, использование возможных путей оптимизации) происходит медленнее, чем делать это самостоятельно? Хуже всего то, что в своей первой версии вы утверждали, что сохранение и восстановление всех атрибутов контекста происходит быстрее, чем просто изменение его CTM. Самые медленные изменения в 2D-контексте обычно связаны со стилем, а не с CTM, который обычно мало что делает, пока операции рисования не будут фактически выполнены.

Kaiido 16.08.2024 04:22

Другие вопросы по теме