Como fazer um relogio com horas e minutos que funciona de verdade
Muita gente tenta montar um widget de clock pra página web e trava logo na primeira hora. O problema não é o conceito, é que os tutoriais ensinam a coisa errada. Você vai achar um monte de código que usa setInterval pra atualizar o DOM inteiro toda vez, e isso é um erro porque o navegador tem que repaintar o canvas ou os elementos HTML a cada tick. Para um relogio com horas e minutos que precisa rodar suave em qualquer dispositivo, o caminho mais limpo é usar requestAnimationFrame e desenhar direto num elemento canvas.
Configurando o canvas e o loop de renderização
O canvas é um elemento HTML5 que permite desenho vetorial via JavaScript. Você não precisa de bibliotecas externas. Crie um arquivo index.html com um tag canvas de 300 por 300 pixels e vincule o JavaScript no final do body. O loop principal funciona assim: limpar a tela, calcular os ângulos das ponteiras baseado na hora atual, desenhar cada ponteira, e pedir ao navegador o próximo frame. O truque aqui é que o requestAnimationFrame não dispara uma vez por segundo. Ele dispara na taxa de atualização do monitor, que geralmente é 60Hz. Isso significa que a função de desenho roda 60 vezes por segundo, mesmo que o tempo só mude a cada segundo. Não há problema nisso, mas você precisa saber disso pra não se confundir quando os números parecerem atualizar demais.
Para calcular o ângulo dos ponteiros, use a matemática básica de trigonometria. O ponteiro das horas gira 360 graus em 12 horas, então cada hora vale 30 graus e cada minuto acrescenta meio grau ao ponteiro. O ponteiro dos minutos gira 360 graus em 60 minutos, então cada minuto vale 6 graus. A forma mais direta é converter tudo pra radianos com a fórmula angulo = valor * Math.PI / 180 e depois usar Math.cos() e Math.sin() pra determinar as coordenadas x e y da ponta de cada ponteira.
Implementação prática com contexto 2D
Eu já passei por um problema específico com esse tipo de implementação. Tinha um projeto onde o relógio precisava mostrar horário em fuso diferente doUTC, e o código simplesmente pegava new Date() sem considerar timezone. O resultado era um relogio com horas e minutos que mostrava a hora local do servidor, não a do usuário. A solução foi usar toLocaleTimeString() passando o locale correto, ou então calcular manualmente o offset usando date.getTimezoneOffset() e somar ao UTC. Outro detalhe que todo mundo ignora é a espessura das linhas. Se você desenhar a ponteira das horas com uma linha de 2 pixels, ela vai sumir na tela. O ponteiro das horas precisa ter cerca de 6 pixels de espessura no canvas 300x300, enquanto o dos minutos pode ser 4 pixels e o dos segundos 2 pixels. Use context.lineWidth antes de cada context.stroke() e defina context.lineCap = 'round' pra que as pontas fiquem arredondadas e o relógio tenha cara de legítimo.
O centro do canvas deve estar exatamente no ponto 150,150 se o elemento tiver 300x300 pixels. Se você errar o centro, as ponteiras vão girar deslocadas e o relógio fica visualmente quebrado. Eu já vi gente gastar duas horas procurando bug onde o problema era simplesmente translate(148, 148) em vez de translate(150, 150).
👉 Clique no botão abaixo para saber mais sobre o assunto!
O problema da precisão e quando ele importa
Aqui vai algo que ninguém conta: esse tipo de relógio nunca será preciso como um relógio atômico porque depende do Date() do navegador, que por sua vez depende do sistema operacional. Em dispositivos móveis, se a tela ficar em modo economia de energia, o requestAnimationFrame pode ser throttled para 30Hz ou até menos. O relógio ainda vai funcionar, mas o movimento das ponteiras parece engasgado. Se você precisa de precisão de timestamp real, esse approach não serve e o certo é usar Web Workers com setInterval de alta frequência somado a um cronômetro interno. Outro ponto cego: se o usuário mudar o tamanho da janela, o canvas não se redimensiona sozinho. Você precisa escutar o evento resize e chamar canvas.width = window.innerWidth junto com canvas.height = window.innerHeight, e então ajustar todos os cálculos de coordenadas proporcionalmente. Sem isso, o relógio vai ficar esticado ou comprimido dependendo do tamanho da viewport.
Para quem quer apenas um widget simples que funcione em qualquer lugar sem instalar nada, basta copiar o código abaixo num arquivo HTML e abrir no navegador. Ele não tem dependências externas e roda offline.
relogio com horas e minutos - código pronto
O código completo está disponível pra quem quer testar imediatamente. A lógica principal cabe em cerca de 80 linhas de JavaScript. O que mais consome código são os_ticks_ do mostrador, que são pequenos segmentos desenhados a cada 5 minutos ao redor da borda do canvas. Se você não precisa dos ticks, pode pular essa parte e o código cai pra menos de 50 linhas. Uma variante útil é transformar esse relógio analógico num relógio digital com display de horas e minutos usando context.font e context.fillText(). Aí você usa a mesma função de tempo, mas desenha texto em vez de linhas. É mais fácil de implementar e funciona em telas pequenas onde ponteiros finos são difíceis de ler. Se o seu objetivo é funcionalidade pura e não estética, o digital é a escolha mais sensata.
Limitações que você precisa saber antes de usar
Esse tipo de relógio via canvas não é acessível por padrão. Um leitor de tela não consegue ler o que está desenhado num canvas porque o conteúdo é puro pixels, não texto estruturado. Se o projeto precisa cumprir WCAG, você precisa adicionar um elemento aria-label ou um span oculto com o horário atualizado via JavaScript. Sem isso, o relogio com horas e minutos funciona perfeitamente pra quem vê e não funciona de forma alguma pra quem depende de tecnologia assistiva. Também não adianta esperar que esse código rode bem em tabs que ficaram abertas por semanas sem interação. O requestAnimationFrame pára quando a aba fica inativa em muitos navegadores modernos, e quando o usuário volta pra aba, o relógio pode mostrar um horário defasado por alguns segundos até o loop retomar. Se isso é um problema pro seu caso de uso, use setInterval em vez de requestAnimationFrame, aceitando que o movimento das ponteiras pode ter micro-travamentos em hardware mais lento.
Se você só quer um relógio funcional sem mexer com código, a alternativa mais direta é usar uma biblioteca como o analog-clock no npm ou o jQuery-clock nos projetos jQuery, mas essas opções introduzem dependência externa e overhead desnecessário se o único requisito for mostrar horas e minutos. Pra coisa simples, código próprio sempre vence em leveza e controle.