Home, Inicio João Gomes Mota contacto Web Design
JavaScript
INVESTIGAÇÃO FOTOGRAFIA WEB DESIGN DESCOBERTAS
English English

Como fazer um Calendário de Advento em JavaScript

  1. Introdução
  2. Arquitectura da aplicação
  3. Preparação
  4. Criação da página HTML
  5. Cookies
  6. Inicialização
  7. Abrir e fechar janelas
  8. Calendário completo ?

1. Introdução

Nestas páginas mostra-se, passo a passo, como se pode fazer um Calendário de Advento em JavaScript. O objectivo principal é ilustrar a utilização de programação e web design para criar uma aplicação funcional; por isso, o Calendário pode parecer esteticamente rudimentar aos olhos dos leitores.

O que é o Calendário de Advento ?

Um Calendário de Advento é um objecto associado ao Natal, típico dos países do Centro e do Norte da Europa.

Para os cristãos, o Advento é o período de quatro domingos que antecede o Natal no dia 25 de Dezembro.

Com um Calendário de Advento prepara-se a chegada do Natal, representando cada um dos dias de 1 a 24 de Dezembro por uma porta ou janela numerada. A cada dia que passa, abre-se a porta com o número correspondente, revelando uma surpresa. Consoante a cultura e religião das famílias e a idade dos destinatários - na maioria dos casos são crianças - esta pode ser um doce, uma pequena prenda, um desenho, uma história alusiva ao Advento, etc..

Se num calendário material as surpresas são materiais, num calendário na Web as surpresas deverão ser imagens, imagens animadas, sons, textos, filmes ou aplicações. Neste caso, usou-se uma solução muito simples: uma única imagem dividida num mosaico de 24 partes, uma por cada dia. À medida que os dias passam, o mosaico completa-se:

No dia 12
fig.1 - O mosaico com os dias 1 a 12 "abertos"

Se se procurar abrir uma "janela" de uma data posterior ao dia actual, aparece por instantes um boneco de neve, sugerindo paciência:


fig.2 - No dia 12, faltam dois dias para se poder abrir o dia 14

Quando se chega ao dia 24 e todas as "janelas" estão abertas a imagem completa-se, desaparecendo o reticulado do fundo.

 

2. Arquitectura da aplicação

Conhecido o objectivo e a operação da aplicação, o primeiro passo é fazer a sua especificação funcional, definindo uma arquitectura que contemple os módulos internos, os interfaces entre módulos e o utilizador, etc. de modo a atingir os objectivos. Como esta a aplicação é elementar, bastou definir quatro regras:

Uma página para o calendário e uma página para o resultado final.
Dada a simplicidade do calendário, esta solução pareceu adequada; além disso, a página final é quase idêntica à página do calendário.
O calendário ocupa o centro da página, restando um rodapé para as ligações às páginas de apoio.
Todos os links têm sempre efeito
Esta é uma regra fundamental da web - por vezes descurada - que moldou o interface da aplicação: o utilizador deve receber sempre um eco dos links "clicados", qualquer que seja o estado da aplicação.
Se é óbvio que a porta se deve abrir ao clicar, revelando o elemento do mosaico, já não é óbvio o que se deve fazer quando se clica de novo a imagem com o link. Para evitar esta ambiguidade, ao clicar numa porta aberta, "fecha-se" a porta.
Além disso, quando se clica numa janela que não pode ainda ser aberta porque o dia correspondente ainda não chegou, surge o boneco de neve, permitindo deste modo dar uma resposta clara à acção do utilizador.
O estado anterior do calendário é guardado
O objectivo do Calendário de Advento é abrir uma janela por dia. Daí que seja importante armazenar o estado do calendário para que não seja necessário reiniciar o processo a cada novo dia.
Para este efeito, usaram-se cookies.
As imagens serão reutilizadas
Para acelerar o tempo de download e melhorar a transição entre as duas páginas da aplicação, as imagens da primeira página serão reutilizadas na segunda página.
Assim, quando o calendário está completo, passa-se para a segunda página suavemente pois a maioria dos conteúdos já está em cache.
Para ver o calendário completo, abra-o numa nova janela.

3. Preparação

Antes de criar as páginas HTML, é necessário um trabalho de preparação prévia dos elementos visuais:

  1. Escolher uma fotografia.
    A dimensão da fotografia - ou qualquer imagem - é definida de acordo com o tamanho do monitor. Optou-se por uma fotografia ligeiramente inferior a 800x600 pixels, pois esta é a resolução mínima mais comum na maioria dos monitores.
  2. "Cortar" 24 janelas na fotografia.
    Optou-se por definir uma sequência aleatória para manter o efeito de mosaico. As imagens da janelas podem ter tamanhos diferentes, mas neste caso optou-se por uma solução mais simples com 24 quadrados iguais.
    Optou-se ainda por manter um espaçamento entre as "janelas" para manter um modelo reticulado, até que a fotografia se complete. Nessa altura o reticulado desaparece.


fig.3 - Modelo da fotografia com as 24 janelas cortadas

  1. Guardar as margens da fotografia em imagens individuais para completar na versão completa, no caso de se pretender o efeito reticulado. Neste exemplo, há cinco imagens com a largura da fotografia e 28 imagens com a altura das "janelas".
  2. Desenhar 24 portas numeradas para ocultar os elementos do mosaico, usando um programa de edição de imagem.
    As portas devem ter as mesmas dimensões das imagens que ocultam.
  3. Desenhar uma porta para os dias que não podem ainda ser abertos. Para este caso, usou-se o boneco de neve (ver fig. 2).

O código da página do calendário será explicado nas próximas secções, indicando-se à esquerda as amostras de código relevantes.

 

4. Criação da página HTML

Após a preparação das imagens para o calendário, é necessário codificar a página HTML com o calendário.

<tr>
  <td colspan="13"><img src="pix2.gif" width="750" height="12"></td>
</tr>
<tr>
  <td><img src="pix2.gif" width="15" height="110"></td>
  <td><A HREF="javascript:dia(12);"><img NAME="i12" src="12.gif" width="110" height="110" border="0" alt="12"></A></td>
  <td><img src="pix2.gif" width="12" height="110"></td>
  <td><A HREF="javascript:dia(19);"><img NAME="i19" src="19.gif" width="110" height="110" border="0" alt="19"></A></td>
  <td><img src="pix2.gif" width="12" height="110"></td>
  <td><A HREF="javascript:dia(3);"><img NAME="i3" src="3.gif" width="110" height="110" border="0" alt="3"></A></td>
  <td><img src="pix2.gif" width="12" height="110"></td>
  <td><A HREF="javascript:dia(8);"><img NAME="i8" src="8.gif" width="110" height="110" border="0" alt="8"></A></td>
  <td><img src="pix2.gif" width="12" height="110"></td>
  <td><A HREF="javascript:dia(21);"><img NAME="i21" src="21.gif" width="110" height="110" border="0" alt="21"></A></td>
  <td><img src="pix2.gif" width="12" height="110"></td>
  <td><A HREF="javascript:dia(2);"><img NAME="i2" src="2.gif" width="110" height="110" border="0" alt="2"></A></td>
  <td><img src="pix2.gif" width="15" height="110"></td>
</tr>

A grelha inclui as imagens visíveis com as janelas e a imagem transparente <img src="pix2.gif"> que é usada como espaçador, variando a dimensão de acordo com as margens da fotografia.

A apresentação da grelha do calendário baseia-se numa tabela de quatro linhas com seis janelas em cada linha.

O código à esquerda corresponde à primeira linha do calendário, com os dias 12-19- 3- 8-21- 2.

Cada célula <TD>...</TD> contém uma imagem, e as imagens da grelha têm uma designação "NAME=i..." associada, para que as funções JavaScript possam manipulá-las. A dimensão das imagens é igual para todas as janelas: quadrados de 110 pixels de lado. A ajuda "alt=.." serve para guiar os utilizadores depois da janela estar aberta.

As ligações (links) das janelas que se abrem estão associadas ao comando JavaScript HREF="javascript:dia(...);" que permitirá alterar as imagens quando o utilizador "clica" nas imagens.

Há um espaçador vertical de 12 pixels de largura entre cada duas janelas vizinhas na mesma linha. No início e no fim da linha, os espaçadores têm 15 pixels de largura. As linhas estão separadas por um espaçador de 750 pixels de largura e 12 pixels de altura (cf. a primeira linha do código).

O calendário é rematado com links para

  1. esta página
  2. a explicação em inglês e
  3. uma comando para inicalizar o calendário, fechando todas as janelas.

 

 

5. Cookies

Todo o calendário se apoia no conceito dos cookies. Um cookie serve para armazenar ao estado do calendário no computador do utilizador, de modo a que a cada nova visita este encontre o calendário tal como o deixou. Se mudar de computador ou de browser, então o calendário surgirá no estado inicial com todas as janelas fechadas.

Embora seja possível usar o calendário num browser que não aceite cookies, a experiência é menos agradável.

document.cookie= "advent=000000000000000000000000;";

Um cookie tem duas partes: o nome e uma string de caracteres. É ainda recomendada uma terceira parte que contém o prazo de validade do cookie. Neste caso, o nome é advent= e a string é um conjunto de 24 algarismos, um por cada janela:

  • "0" para uma janela fechada e
  • "1" para uma janela aberta

Se o utilizador não aceitar cookies, o estado das janelas não é armazenado e é necessário recomeçar o processo a cada novo dia.

hoje = new Date(),
janelas = new Array(25),
isCookie;

function init() {
var i,c=0;

6. Inicialização

O programa usa três variáveis globais:

  • a data hoje,
  • um array com o estado de cada uma das janelas e
  • uma flag que assinala se os cookies são aceites pelo utilizador.

if(document.cookie.length>0) isCookie=true;
else {
 document.cookie="advent=000000000000000000000000;";
 if(document.cookie.length>0) isCookie=true;
 else isCookie=false;
}

O primeiro passo é verificar se o cookie para estão activos. Se estiverem, isCookie=true. Caso contrário, inicializa-se o cookie. Se ainda assim, os cookies não ficarem activos, significa que o browser está não aceita cookies.

ckOffset=document.cookie.indexOf("advent=")+6;
O passo seguinte é calcular o índice da string onde começa a sequência de 24 flags. Este valor varia entre browsers.

if(isCookie) {
  for(i=1;i<25;i++) {
    janelas[i]=document.cookie.charAt(ckOffset+i);
    if(janelas[i]=='1') {
      len=document.images['i'+i].src.length;
      document.images['i'+i].src = document.images['i'+i].src.substr(0,len-3)+"jpg";
    }
  }
 
teste();
}
return;
}

O remanescente da inicialização só se aplica no caso dos cookies estarem activos.

Nesse caso, a string dos cookies é percorrida e copiada, posição a posição, para o array janelas[]. Note-se que o array começa na posição 0 (onde se guarda o sinal '='), enquanto as janelas do calendário começam na posição 1.

Se uma das posições da janela ficar a '1', a imagem correspondente com o número do dia é trocada pela fotografia.

Nota importante: nesta passagem, é necessário que as imagens das janelas fechadas e as das janelas abertas tenham exactamente o mesmo nome, com excepção da extensão. Para este exemplo, usou-se JPG para as fotografias e GIF para as janelas fechadas.

3.gif 3.jpg

fig.4 - A janela fechada e a janela aberta correspondente.

Uma vez reposta as janelas no estado em que o utilizador as tinha deixado na última visita, faz-se um teste para determinar se o calendário está completo.

function reiniciar() {
if (document.cookie.length>0) document.cookie = "advent=000000000000000000000000;";
document.location = "1.html";
return;
}

O utilizador pode reinicializar o calendário usando a função reiniciar(). Trata-se, simplesmente, de repor o cookie a 0000 e recarregar a página no browser com o comando document.location="1.html".

 

7. Abrir e fechar janelas

A abertura e fecho das janelas são o coração da aplicação e a cada nova acção sobre a janela, o cookie é actualizado.

As operações realizam-se através da função dia(d), que toma o dia da janela que o utilizador indicou.

function dia(d) {
var str="advent=",
    len, i, ano, expira;

if(d <= hoje.getDate()) {
  len=document.images['i'+d].src.length;
  if(janelas[d]=='1') {
    document.images['i'+d].src = document.images['i'+d].src.substr(0,len-3)+"gif";
    janelas[d]='0';
  }
  else {
    document.images['i'+d].src = document.images['i'+d].src.substr(0,len-3)+"jpg";
    janelas[d]='1';
  }

A variável str="advent=" guarda a string do cookie e é logo inicializada. A variável expira guardará a data de expiração do cookie.

O primeira teste verifica se o dia da janela seleccionada é anterior ou igual ao dia do mês nesse momento. Em caso afirmativo, troca a imagem da janela, isto é:

  • se janelas[d]=='1', a fotografia (imagem .JPG) é escondida e "fecha-se" a janela: janelas[d]=='0', mostrando o número (imagem .GIF).
  • se janelas[d]=='0', a imagem com o número (.GIF) é escondida e "abre-se" a janela: janelas[d]=='1', mostrando a fotografia (.JPG).
  for(i=1;i<25;i++) str += janelas[i];

  ano=hoje.getYear();
  if(ano<1000) ano+=1900; // patch NN4
  expira=new Date(ano+1, hoje.getMonth(), hoje.getDate(), hoje.getHours(), hoje.getMinutes(), hoje.getSeconds());

  str += ";expires="+expira.toGMTString();
  document.cookie = str;

  setTimeout("teste()",5000);
}

De seguida actualiza-se o cookie, completando a variável str com cada uma das posições do array janelas[].

Segundo as boas práticas, a cada cookie deve estar associada uma data de expiração. Neste caso, escolheu-se um ano e é necessário calcular essa data.

Algumas versões do Netscape Navigator 4 tinham um bug na função .getYear() e devolviam apenas dois algarismos. Pode ultrapassar-se este problema com o patch indicado, embora este problema seja hoje residual.

De seguida, a string com a data de expiração é acrescentada a str e o cookie é escrito.

Finalmente, após aguardar 5 segundos, é chamado o teste para detectar se o calendário está completo.

else {
  document.images['i'+d].src='janela_fechada.gif';
  setTimeout("espera("+d+")",1000);
}
return;
}


function espera(d) {
document.images['i'+d].src=d+'.gif';
return;
}

Se o dia escolhido for superior ao dia do mês no momento, a imagem com o número é trocada pela imagem 'janela_fechada.gif' e, após 1 segundo, chama-se a função espera, indicando a janela seleccionada.

A função espera(d) limita-se a repor a imagem com o número do dia na janela seleccionada.

 

8. Calendário completo ?

Sempre que uma janela muda de estado, verifica-se se as 24 janelas estão abertas e, se tal acontecer, chamar a atenção do utilizador.

function teste() { // testar as 24 condições
var i, completo=true;

if(hoje.getDate()>=24) {
  for(i=1;i<25;i++) if(janelas[i]!='1') {
    completo=false;
    
break;
  }
  if(completo) document.location="25.html";
}
return;
}

A função teste() verifica se o dia do mês é igual ou superior a 24 e se as 24 janelas estão abertas. Note-se que o encadeamento de condições corresponde à conjunção lógica.

Esta implementação não permite terminar a aplicação depois de 1 de Janeiro, pois a primeira condição falha. Se se pretender que o calendário fique sempre aberto, há que eliminar a primeira condição do teste. Caso contrário, o calendário só fica "completo" a partir do dia 24 de cada mês.

Há múltiplas formas de verificar se as 24 janelas estão abertas. A mais simples é testar as 24 condições em sequência.

function teste() { // somar o índice das janelas abertas
var i,c=0;
if(hoje.getDate()>=24) {
  for(i=1;i<25;i++) if(janelas[i]=='1') c+=i;
  if((c==300)) document.location="25.html";
}
return;
}
Porém, ao escrever esta aplicação, lembrei-me de uma história sobre o matemático Gauss e as progressões aritméticas e escolhi outra variante: somar o índice de todas as janelas abertas e verificar o total.
Sobre este Presépio

O Presépio da imagem é feito com figuras de barro populares, compradas em feiras portuguesas.

Este é um dos tipos de Presépio mais comuns nas regiões rurais de Portugal: um tapete de musgo macio que se recolhe nas florestas, sobre o qual se espalham as figuras do Natal: Jesus, Maria, José, o burro e a vaca, os Magos e os pastores.
A tradição popular acrescenta muitas figuras do imaginário português: castelos, igrejas, pontes, casas caiadas, etc..

Nesta aplicação, o "prémio" que o utilizador recebe ao completar o calendário, é vê-lo numa imagem única sem reticulado.

Para tal, criou-se uma segunda página, com o nome 25.html, com um layout rigorosamente igual ao da primeira, onde os espaçadores transparentes são substituídos pelas imagens que completam o mosaico:


fig.4 - O mosaico completo

  1. Introdução
  2. Arquitectura da aplicação
  3. Preparação
  4. Criação da página HTML
  5. Cookies
  6. Inicialização
  7. Abrir e fechar janelas
  8. Calendário completo ?
Se tiver dúvidas, dificuldades ou comentários a sobre esta aplicação, contacte-me.
©2004-05 João Gomes Mota
Home, Inicio  → WEB DESIGN → APLICAÇÕES
Criado em Dezembro 2004. Última alteração: Março 2005. inicio da página