Criando um relógio no estilo iOS usando o canvas
Parte 2: Desenhando os elementos do relógio
Como todo projeto organizado, precisamos definir os objetivos, levantar o que será necessário, preparar as ferramentas. Com nosso relógio não será diferente. Primeiro, vamos começar desenhando e definindo os primeiros elementos de nosso relógio como o círculo que irá compor o background, os números e os ponteiros de horas, minutos e segundos.
O código fonte mostrado neste post e nas próximas 2 partes são trechos do projeto completo, que será disponibilizado para download ao publicarmos a 4ª parte. Então alguns trechos poderão não fazer sentido inicialmente, mas basta acompanhar e aos poucos o relógio ganhará movimento e será finalizado.
Alguns métodos e propriedades nativas do canvas que serão utilizados, estão abaixo classificados quanto a sua função :
Definir cores, estilos para cores e sombras para elementos
fillStyle | Defini a cor, gradiente ou padrão(imagem) usado para preencher o desenho |
strokeStyle | Defini a cor, gradiente ou padrão(imagem) usado em bordas |
shadowColor | Defini a cor usada para sombra |
shadowBlur | Defini o nível de "blur" ou "desfoque" da sombra para tornar mais real |
shadowOffsetX | Defini a distância horizontal da sombra em relação ao elemento |
shadowOffsetY | Defini a distância vertical da sombra em relação ao elemento |
createLinearGradient() | Cria um gradiente linear usado em um contexto canvas |
createRadialGradient() | Cria um gradiente radial ou circular usado em um contexto canvas |
addColorStop() | Método complementar dos gradientes linear e radial para adicionar e misturar diferentes cores |
Definir estilo para linha simples
lineWidth | Defini a largura de uma linha |
Criar Retângulos e limpar pixel em um determinado retângulo
rect() | Criar retângulos definindo largura, altura, posição horizontal e vertical |
fillRect() | Semelhante ao método "rect()", com a diferença que este desenha um retângulo preenchido(cheio). |
clearRect() | Limpa pixels de um determinado retângulo |
Definir caminhos para criação de formas e elementos
fill() | Preenche uma forma no caminho em contexto |
stroke() | Desenha uma borda no atual elemento em contexto |
beginPath() | Defini ou reinicia o início do caminho |
closePath() | Fecha o caminho atual retornando para o anterior |
moveTo() | Move todos os elementos do caminho horizontalmente e verticalmente em relação ao eixo das abscisas e ordenadas do canvas |
lineTo() | Adiciona pontos criando uma reta(linha) a partir do último ponto definido na tela |
arc() | Cria arcos, círculos ou ate mesmo parte de círculos. |
Transformações em elementos, para tamanhos e posições
scale() | Defini escalas para um desenho, aumentando ou diminuindo sua proporção |
rotate() | Defini em quantos graus um desenho irá rotacionar, em graus radianos |
translate() | Defini em quantos pixels será deslocado horizontalmente ou verticalmente um desenho |
Escrever textos, definir fontes e estilos de cores
font | Defini a família da fonte e outras propriedades como tamanho para o texto em contexto |
textAlign | Defini o alinhamento do texto em contexto, exemplo, a esquerda, a direita ou centralizado |
textBaseline | Defini a base do texto em contexto, exemplo, meio. |
fillText() | Desenha um texto preenchido na tela do canvas, definindo sua posição horizontal e vertical em relação ao eixo zero das abscisas e ordenadas do canvas |
Composição de opacidade ou transparência em elementos
globalAlpha | Defini o alfa da transparência para elementos em contexto |
Métodos para definição de contextos e armazenamento de informações
save() | Salva o contexto atual do canvas |
restore() | Restaura o contexto anterior ao atual salvo |
Desenhando o círculo drawBackgroundClock()
Algumas das variáveis utilizadas no escopo desta função são:
ang | Armazena o valor em graus radianos de 180º |
pi | Armazena o valor de 2 ? ( onde PI vale aproximadamente 3,14...) |
bg | Armazena uma referência ao objeto background que contém as cores do relógio |
Para desenhar nosso relógio, iremos montar 4 círculos sendo eles dois completos e dois meia lua, para formar efeitos de gradiente e deixar com um realismo mais próximo do iOS versão 6.
Nas linhas 91, 92 e 93 acima, eu defino três variáveis para uso interno da função, que desenhará o círculo. Sendo uma delas a "ang" para guardar o valor de um ângulo de 180 graus que será usado em duas circunferências do background. E outro valor guardado, será a variável "bg", um objeto já definido no início do projeto que contém as cores desse background, onde há a possibilidade de alteração dessas cores através de valores já definidos. Se você não conhece objetos em javascript, confira no link Objeto e funções construtoras mais informações sobre o assunto.
Para efeito de reusabilidade, definimos uma função genérica para criação de um círculo. Através dela, definiremos cor, borda, início do ângulo, fim do ângulo da circunferência, posição x do círculo, posição y do círculo, se vai possuir escala em x ou em y, e também se irá possuir efeito radial em sua cor. Definidas desta forma a função circle() assume os seguintes valores:
color | Se definido, preenche o círculo com a cor escolhida em hexadecimal ou RGB |
ang | Início do ângulo(radianos) para formar o círculo |
end_ang | Fim do ângulo(radianos) para formar o círculo |
x | Distância horizontal em pixels do círculo em contexto |
y |
Distância vertical em pixels do círculo em contexto |
scX | Valor da escala horizontal para o círculo em contexto |
scY | Valor da escala vertical para o círculo em contexto |
radial | Definir se o círculo conterá efeito gradiente radial |
Nas linhas 95 a 123, está escrita a função que desenha um círculo no canvas contendo todas as informações descritas no parágrafo anterior. A importância de possuir uma função genérica para criar um círculo é justamente evitar repetição de código milhares de vezes para a mesma finalidade, desta forma o código fica limpo e mais prático. Claro, que esta função não está perfeita, mais detalhes poderiam ser informados via parâmetro, mas este não é nosso objetivo, então para exercício de casa fica a dica para tornar aberta a escolha de tal tipo de radial, tamanho da borda e entre outras informações.
As quatro últimas linhas 125, 126, 127 e 128 são nossos círculos desenhados que compõem o background do relógio.
A primeira linha 125, é a borda do nosso relógio definida na cor cinza(#CCC) por padrão dentro da função, funcionando assim, se o a cor for informada, a circunferência assumirá a cor desejada, caso omitido a cor, cria a borda por padrão.
A segunda linha 126, é o cinza mais claro (#F3F2F2), que preencherá nosso relógio todo ficando desta forma:
Se tudo ocorreu certinho, retornando para as linhas de códigos, 127 e 128 finalizam nosso background, desenhando duas circunferências em forma de meia lua, de forma, que uma terá uma meia lua um pouco maior que a outra para formar um efeito de radial personalizado, uma espécie de curva no nosso relógio. Veja o resultado.
Depois de executado, este será nosso background para o relógio durante o dia. Agora vamos para os números.
Desenhando e posicionando os números drawHours()
Algumas das variáveis utilizadas no escopo desta função são :
i | Controladora da iteração dos números 1 ao 12 |
x | Posição horizontal em pixels do número em contexto |
y | Posição vertical em pixels do número em contexto |
ang | Ângulo em radianos do número em contexto |
dif | Diferença que será utilizada no cálculo do ângulo em relação ao total |
total | Total de números corrente que serão trabalhados |
percentScreen | Valor de 10% porcentos da tela armazenado |
distance | Distância correspondente de uma extremidade a outra da circunferência dos números |
radiusHours | Raio da circunferência dos números |
font |
Fonte definida para os números(Trebuchet MS) |
Para desenhar os números e posicioná-los corretamente em nosso relógio, vamos lembrar um pouco da Trigonometria estudada no ensino médio. A figura abaixo mostra um círculo com as posições em graus, para os locais dos números do relógio (lembre que o círculo possui 360 graus, divididos em 12, nos dão intervalos de 30 graus):
Nosso problema é encontrar a posição do canvas onde os números do relógio devem ser colocados. Vamos ver, por exemplo, o número 2. No desenho abaixo, vemos que o número 2 está a 30º de distância do número 3 (que fica exatamente sobre o eixo X):
O local onde o número 2 está é exatamente uma posição x,y sobre o círculo. Por definição, o tamanho x é o cosseno do ângulo formado entre o eixo horizontal e a seta que indica o número 2, ou seja, cos(30º). A altura do número 2 é o valor y que, por definição, é o seno do ângulo indicado, ou seja, sen(30º). Veja isso na imagem abaixo:
Assim, temos que é fácil encontrar as posições x,y de qualquer um dos números do relógio, sabendo o ângulo de distância entre ele e o número 3 (posição 0º, segundo os padrões adotados).
Temos então que, para qualquer número do relógio, os valores de x e y serão, respectivamente, o coseno e o seno do ângulo alfa formado em relação ao eixo horizontal, conforme mostrado na figura:
x,y = cos(?),sen(?)
Seguindo, esta ideia, a posição do número 1, a 60º do eixo x, será
x = cos(60º) e y = sen(60º)
Implementando o cálculo trigonométrico em JavaScript
O cálculo trigonométrico em JavaScript trabalha com ângulos na forma de radianos. Precisamos então transformar os graus em radianos. Isso é feito sabendo que a circunferência mede 2 ? r, onde r é o raio do círculo e ? é a constante (aprox.) 3,14.
Com o ângulo em radianos, encontramos a posição dos ponteiros, aplicando na fórmula :
x = DISTÂNCIA x RAIO x COS(ÂNGULO)
y = DISTÂNCIA x RAIO x SEN(ÂNGULO)
Onde, x e y correspondem respectivamente a distância exata em pixel em relação ao centro do círculo.
DISTÂNCIA = distância de uma ponta a outra do círculo
RAIO = distância em relação centro do círculo
ÂNGULO = ângulo que será calculado em radianos
Aplicando esta fórmula, todos os 12 números serão desenhados corretamente em nosso relógio. Para melhorar a apresentação aplicamos uma fonte já definida no início do nosso projeto (Trebuchet MS), guardada na variável font. O tamanho da fonte foi definido em 10% da largura da tela, guardada na variável percentScreen. Desta forma, qualquer tamanho do relógio terá números proporcionalmente adaptados.
Enfim, nosso relógio ganha uma cara nova ficando desta maneira :
Desenhando os ponteiros de horas, minutos e segundos drawHoursLine(), drawMinutesLine() e drawSecondLine()
Umas das fases mais simples do projeto é desenhar os ponteiros, pois o canvas permite desenhar livremente circunferências, linhas, retângulos e entre outros elementos já definidos. Utilizaremos o recurso de criar uma forma própria, neste caso, um triângulo. E para isso, definimos uma função genérica para criar os triângulos que serão nossos ponteiros da hora e do minuto. O ponteiro dos segundos terá uma largura mais fina do que os outros, ficando em forma de uma reta.
Veja o código:
A função para desenhar nossos triângulos é drawPonteiro() nas linhas 167 a 187 do projeto correspondem ao escopo da função, ela recebe algumas informações como o "pointer" ou ponteiro no instante, assim como, quantos ângulos este ponteiro irá se deslocar, a diferença entre o instante do ângulo e o total, o total, altura do triângulo, largura do triângulo, cores 1 e 2 para fazer gradiente, posicionar tantos x em relação ao centro e informar o tipo de gradiente. Assim Definida ela assumirá os seguintes valores:
pointer | Um número que correspondente o determinado instante do ponteiro |
dif | Diferença entre o número atual e o total utilizado no cálculo do ângulo |
total | Total de números que serão convertidos em graus radianos para formar os ângulos |
heightLine | Altura do ponteiro em pixels |
widthLine | Largura do ponteiro em pixels |
color1 | Cor usada no efeito linear gradiente |
color2 |
Cor usada no efeito linear gradiente |
x | Distância horizontal em pixels em relação ao centro da tela |
g | Tipo de gradiente e configurações de posicionamento |
Assumindo estes valores, podemos criar nossos ponteiros, é só conferir no projeto que existe três funções para desenhar o ponteiro da hora, minuto e segundo, respectivamente nomeados por, drawHoursLine(), drawMinutesLine(), drawSecondsLine(). Que irão utilizar a função drawPonteiro(), estas funções estão dividas justamente em cada módulo para um ponteiro diferente, justamente para alterar as configurações de algum ponteiro isoladamente.
As funções drawHoursLine() e drawMinutesLine() desenham um triângulo na tela a partir do centro sendo que o ponteiro da hora tem uma altura menor que do minuto. Todas informadas através de porcentagem em relação a tela, para que os ponteiros se adaptem ao tamanho que for selecionado para o relógio.
1. drawHoursLine() e drawMinutesLine()
A função do ponteiro das horas, precisa respectivamente do atributo hours do objeto clock, que será sempre atualizado gerando diferentes ângulos para nosso ponteiro. E em seguida, passamos a diferença entre o total de números(três), e o total de números(doze), para cálculo dos ângulos. Com estas informações nosso ponteiro de horas recebe todos os dados necessários para calcular os ângulos do ponteiro.
Desenhar os ponteiros agora e fácil só precisa informar o resto das propriedades de altura, largura, cor e distância horizontal. Para nosso relógio ganhar uma cara nova. Mas para matar dois leões de uma vez só, implementar o ponteiro do minuto, seguirá o mesmo procedimento do ponteiro da hora, com a diferença que o total de números agora será de sessenta e a diferença de quinze, sem falar, que este ponteiro terá uma altura um pouco maior que o ponteiro das horas. Com isso, nosso relógio fica desta maneira:
2. drawSecondsLine()
Feito os ponteiros das horas e minutos, agora adicione a função para o ponteiro do segundo as mesmas configurações do ponteiro dos minutos, com a diferença que agora a largura é 0.75 justamente para não ficar com aparência de um triângulo. A cor que foi definida para este ponteiro foi uma vermelha(#DB3E3E), onde mais tarde a mesma será alterada quando anoitecer.
Adicionada esta última função, veja como nosso relógio ficou :
Perceba que nosso relógio já esta quase pronto, no código completo você poderá conferir alguns recursos a mais implementados no relógio como marcadores central e entre outros recursos, afim de melhorar a visualização dos ponteiros e fazer um efeito de arredondamento para transmitir um pouco de realismo. Estes marcadores, utilizam a mesma ideia que aplicamos no background do relógio. Pois, eles são dois círculos desenhados em cima dos ponteiros.
No próximo post, onde iremos implementar os movimentos dos ponteiros.
PARTE 1: INTRODUÇÃO AO CANVAS
PARTE 2: DESENHANDO OS ELEMENTOS DO RELÓGIO
PARTE 3: ADICIONANDO MOVIMENTO AOS PONTEIROS
PARTE 4: FINALIZANDO O PROJETO
Para ser informado sobre a publicação da continuação, siga-nos no Twitter