L'animació dels objectes que es visualitzen en la pàgina web és un dels efectes més apreciats pels dissenyadors web. Existeixen moltes formes de donar dinamisme a la pàgina, utilitzen applets, gifs animats, presentacions Flash, etc. Tots aquests elements indicats són externs al navegador i generalment es carreguen com a plug-in's.

En aquest apartat crearem animació, però, sense utilitzar cap element extern. Únicament utilitzarem els recursos que ens proporciona el mateix navegador, és a dir, manipularem les propietats dels objectes en temps de visualització.

Les propietats dels objectes són fàcilment ajustables en temps de disseny, però, per modificar-los en temps de visualització necessitarem l'ajut de JavaScript.

En aquesta pràctica es presenten dues animacions, una sobre objectes imatge i l'altra sobre capes, però, existeixen moltes més animacions que es podem implementar, l'únic límit està en els recursos que ens subministra el navegador i l'habilitat i imaginació del programador.

Ara bé, el problema més greu serà la compatibilitat entre els diferents navegadors, hi haurà coses que no es podran fer en alguns navegadors i coses que es podran fer en tots però de forma diferent. Haurem de saber en quin navegador es visualitza i si l'efecte no és compatible haurem de mostrar algun altre contingut alternatiu.

  Elements del llenguatge en aquest capítol
Mètode eval() : Avalua el contingut d'una cadena de text.
Propietat height : Conté l'altura d'un objecte.
Propietat width : Conté l'ample d'un objecte.
Propietats left i top : Permeten assignar la posició horitzontal i vertical d'un objecte
Propietat offsetLeft i offsetTop: Conté la posició horitzontal i vertical d'un objecte (NS6, IE)
Propietat clientWidht i clientHeight : Conté l'ample i l'alt de la finestra (IE)
Propietat innerWidth i innerHeight : Conté l'ample i l'alt de la finestra (NS)
Propietat src : Assignació del fitxer imatge a un objecte Imatge.
Propietat visibility : Indica si l'objecte serà visible o no.
Mètode Math.round() : Arrodoneix al sencer més proper un valor numèric.
Mètode Math.random() : retorna un valor numèric aleatori entre 0 i 1.
Propietat zIndex : Indica la coordenada Z d'un objecte (enter).
Operador ? : : Operador condicional ternari.
Directiva <style> : Permet definir els estils CSS.
Objecte document.layers : Objecte de Netscape 4.
Objecte document.all : Objecte d'Internet Explorer.
Objecte document.getElementById : Objecte de Netscape 6.
Mètode document.captureEvents() : Mètode per definir la captura d'events (NN)
Objecte window.event : Objecte que conté informació sobre l'últim event (IE)
Events onmouseup i onmousemove
Propietats pageX, pageY : retornen la posició del ratolí (a NS)
Propietats clientX, clientY : retornen la posició del ratolí (a IE)
 
1.-
Commutació d'imatges.
Aquest exemple fa un intercanvi d'imatges fusionant-les entre si. Tot l'efecte consisteix en manipular amb JavaScript les propietats de dos objectes "Image". Les propietats que es manipulen en temps de visualització són l'alçada (height), l'amplada (width) i la seva coordenada Z (z-index).

Les dues imatges estan situades en el mateix espai físic. Mentre una imatge va augmentant la mida, l'altra la va disminuint. Al mateix temps, van alternant la seva coordenada Z per evitar que una d'elles quedi definitivament amagada a sota.

Aquest efecte no funciona amb NN4. Per tant, si detectem que utilitzem aquest navegador mostrarem un contingut alternatiu. Aquest contingut ha d'ésser una alternativa, per tant, si es considera convenient, es pot substituir per alguna altra imatge estàtica.

Tot i que pot funcionar amb els navegadors IE i NN6, la forma d'implementar-lo és diferent. Ajustar aquesta incompatibilitat és el més complicat del codi.

Dins el codi, a les línies 7-9, detectem el tipus de navegador i dins el bloc <body> creem la pàgina d'una forma o una altra segons sigui el navegador (el codi dins el bloc <body> pot anar conjuntament a un altre codi HTML convencional). Les línies 41-51 creen el contingut, si el navegador és NN4, solament s'escriu un text alternatiu d'avís (línia 42), aquest text es pot substituir per algun altre contingut alternatiu.

Les línies 43 a 50 creen el contingut en cas que sigui un navegador NN6 o IE. Aquest contingut consisteix en definir dues imatges amb identificadors img1 i img2 i una capa que conté el botó de commutació. Observeu com les línies 43-50 són la continuació de la cadena iniciada dins de "document.write", per clarificar-ho, s'ha posat la cadena en línies diferents, però, per indicar a l'intèrpret Javascript que la cadena continua a la línia següent, s'afegeix el símbol " \ "en el lloc d'on es parteix.

Les línies 2,3 i 4 defineixen les propietats CSS per a les dues imatges i la capa. La capa botocambia no té cap utilitat en l' efecte del programa, solament s'utilitza per facilitar el posicionament del botó de commutació.

L'accés a les propietats dels objectes es fa de forma diferent amb NN6 i IE. Per exemple:
    - Per llegir el valor de la propietat height, width o zIndex d'un objecte Image es fa així:
                    Per a IE: document.all.ObjecteImage.propietat
                    Per a NS6: document.ObjecteImage.propietat

    - Per assignar un valor a les propietats height, width o zIndex de l'objecte Image es
      fa així:
                    Per a IE: document.all.ObjecteImage.style.propietat = ...
                    Per a NS6: document.getElementById('ObjecteImage').style.propietat = ...

Com es pot veure són formes totalment diferents de llegir o assignar valors segons sigui el navegador, ja que la manera de referenciar l'objecte és molt diferent. La funció refObj() és l'encarregada de facilitar aquestes incompatibilitats. Si la invoquem, ens retorna una cadena per referenciar l'objecte adequadament, segons quin sigui el navegador emprat i segons si volem llegir un paràmetre o el volem modificar (en aquest últim cas hem de passar-li el nom de l'objecte).

La creació de la modificació d'una propietat es fa conjuntament entre la invocació de refObj(), que retorna una cadena, i la concatenació de la resta de paràmetres per formar la cadena. Un cop creada tota la cadena, amb l'ajut de la funció eval(), s'executa el seu contingut. Tal com es pot veure a les línies 17-21, 25-31 i 34-35

La funció cambia() és l'encarregada de fer la manipulació de les propietats, ajudada per la funció refObj() per crear les referències dels objectes. Es pren com a referència les dimensions en píxels de les dues imatges, en aquest exemple: img1 és 343x343 i img2 és 227x343 (les dues imatges poden ser de la mateixa mida o diferent). Fixeu-vos com a la línia 3, s'ha indicat per a la imatge 2 un ample i un alt inicial de 0px, és a dir, inicialment la imatge 2 no existeix.

La variable booleana sentit representa quina és la imatge que ha de créixer i decréixer en cada moment, inicialment val true, això vol dir que la imatge 1 haurà de decréixer i la 2 créixer, un cop feta la transició, s'inverteix el valor d'aquesta variable a la línia 22.

Prenent com a referència la imatge 1, considerem sentit descendent quan aquesta decreix (sentit=true) i ascendent quan aquesta augmenta (sentit=false) i farem que aquesta modifiqui la seva mida en passos de 5 píxels.

La condició de la línia 17 diu : quan imatge 1 decreixi, si la seva alçada és inferior a 5 píxels o bé quan imatge 2 decreixi i la seva alçada sigui inferior a 5 píxels s'acaba la transició i s'assignen els valors finals a totes dues imatges segons sigui el valor de sentit (línies 18-21).

En cas contrari, (línies 25-28) segons sigui el valor de sentit, s'incrementarà o decreixerà el valor d'alçada i amplada de la imatge 1 i 2 amb 5 píxels (excepte l'ample d'imatge 2, que es farà en 3 píxels, ja que proporcionalment l'ample d'imatge 2 és inferior al d'imatge 1). També es farà, a cada pas, un intercanvi de la propietat z-index per evitar que una imatge quedi definitivament oculta per l'altra (línies 29-36). També es programa un temporitzador (línia 37) per repetir el mateix procés invocant la funció "cambia()", al cap de 10 ms., mentre la imatge que decreix no valgui una alçada inferior a 5 píxels.

El codi a implementar és el següent:


Dins el bloc <head>:

1 <style type="text/css">
2   #img1 {position: absolute; left:10px; top:30px; width:343px; height:343px;
                  visibility:visible; z-index:1;}
3   #img2 {position: absolute; left:10px; top:30px; width:0px; height:0px;
                  visibility:visible; z-index:2;}
4   #botocambia {position: absolute; left:218px; top:380px; visibility:visible; z-index:1}
5 </style>

6 <script language="JavaScript">
7   var ns4=document.layers;
8   var ie=document.all;
9   var ns6=document.getElementById;
10  var sentit=true;
11  function refObj(obj) {
12     if (ie) return (obj!=null)?("document.all." + obj) : ("document.all");
13     if (ns6) return (obj!=null)?("document.getElementById(' " + obj + " ')") :
                    ("document");
14   }
15   function cambia()
16   {
17     if ( (sentit && eval(refObj() + '.img1.height<5')) || (!sentit && eval(refObj()
                    + '.img2.height<5')) ) {
18       eval(refObj('img1') + '.style.width = (sentit)?0:343');
19       eval(refObj('img1') + '.style.height= (sentit)?0:343');
20       eval(refObj('img2') + '.style.width = (sentit)?227:0');
21       eval(refObj('img2') + '.style.height= (sentit)?343:0');
22       sentit=!sentit;
23     }
24     else {
25       eval(refObj('img1') + '.style.width = (sentit)?(' + refObj() + '.img1.width - 5):('
                    + refObj() + '.img1.width + 5)');
26       eval(refObj('img1') + '.style.height= (sentit)?(' + refObj() + '.img1.height - 5): ('
                    + refObj() + '.img1.height + 5)');
27       eval(refObj('img2') + '.style.width = (sentit)?(' + refObj() + '.img2.width + 3):('
                    + refObj() + '.img2.width - 3)');
28       eval(refObj('img2') + '.style.height= (sentit)?(' + refObj() + '.img2.height + 5):('
                    + refObj() + '.img2.height - 5)');
29       if(eval(refObj('img1') + '.style.zIndex==1')) {
30         eval(refObj('img1') + '.style.zIndex=2');
31         eval(refObj('img2') + '.style.zIndex=1');
32       }
33       else {
34         eval(refObj('img1') + '.style.zIndex=1');
35         eval(refObj('img2') + '.style.zIndex=2');
36       }
37       setTimeout("cambia();", 10);
38     }
39   }
40 </script>

Dins el bloc <body>:
      ...
41 <script language="JavaScript">
42     if (ns4) document.write("Ep! Aquest efecte no esta adaptat per funcionar
                                                                                                amb NN4");
43     else document.write("\
44           <img id='img1' src='stmaurici.jpg'>\
45           <img id='img2' src='aigtortes.jpg'>\
46           <div id='botocambia'>\
47           <form>\
48             <input type='button' value='Cambia imatge' onClick='cambia();'>\
49           </form>\
50           </div>");
51 </script>
      ...

 
2.- Capes animades.
En aquest apartat veurem com podem manipular els paràmetres de les capes en temps de visualització, aconseguint efectes d'animació.

El codi de l'exemple és molt extens (242 línies), per aquest motiu no s'ha inclòs en aquesta pàgina, per veure el codi, heu de visualitzar la finestra de l'exemple i fent clic amb el botó dret del ratolí sobre la finestra, seleccioneu la "Visualització del Codi Font".

Tot i ser un exemple extens, es pot considerar com si fossin quatre exemples diferents d'animació de capes. Els blocs de codi queden clarament diferenciats ja que fan referència a una única capa de les quatre de mostra.

Capa1.- Desplaçament lateral des de l'esquerra a petició de l'usuari.
Capa2.- Capa que simula el comportament d'una finestra de Windows. Inclou una capa enniuada de nom capa2a.
Capa3.- Capa superior que conté el títol, la seva animació s'inicia automàticament en carregar la finestra i es desplaça progressivament des de l'esquerra fins al centre.
Capa4.- Capa que conté una icona (smiley) que es mou constantment per sobre de la pantalla rebotant aleatòriament en els marges. L'animació s'inicia quan la capa3 s'ha posicionat en el centre de la finestra.

Podem aïllar cadascun dels 4 exemples copiant les línies de codi indicades a continuació en una pàgina web a part. En la nova ubicació s'ha de respectar que també estiguin situades dins els corresponents blocs <style> i <script> de <head> i <body>.

          - Comú a tots els exemples: 14-16.
          - Capa1: línia 7, línies 17-41 i línies 195-212.
          - Capa2: línies 8-9, línies 42-112 i línies 213-234.
          - Capa3: línia 10, línies 113-124 i línies 235-237.
          - Capa4: línia 11, línies 125-191 i línies 238-240.

El codi indicat dins el bloc <body> i la definició d'estils dins la directiva <style> del bloc <head> apliquen els objectes HTML i els valors de les propietats que es veuen, per defecte, en carregar-se la pàgina. En canvi, el codi, dins la directiva <script> aplica la manipulació de les propietats dels objectes HTML en temps de visualització, de manera que obtenim l'animació desitjada.

No es comentaran les diferents formes d'assignar o llegir valors de les propietats dels objectes ja que tal com està fet el codi es pot interpretar molt fàcilment quina és la forma pròpia de cada navegador. Només cal distingir les línies començades amb if (ie) .... , if (ns4) .... i if(ns6) ... que serveixen per discriminar el codi propi per a cada navegador i interpretar la forma en la qual es llegeix i s'assigna cada propietat.

Part comuna:
La part comuna a tot el codi són les línies 14-16, la seva funció és detectar el tipus de navegador IE, NN4 o NN6 amb què es visualitza la pàgina. Per això es testeja l'existència dels objectes document.all, document.layers i document.getElementById propis de cadascun dels navegadors indicats, respectivament. La variable ie, ns4 o ns6 valdrà true en el cas que sigui el navegador corresponent.

La detecció del navegador és necessària perquè l'accés a les propietats de les capes i tractament d'events són diferents per cada cas. Dins el codi propi de cada capa trobarem condicionals que testegen aquestes variables i apliquen la manipulació de la propietat corresponent amb el format apropiat, en cas contrari obtindríem errors.

Aquesta forma de diferenciar la manipulació de les propietats no és l'habitual en programació JavaScript, en aquest exemple s'ha preferit fer-ho així per facilitar la llegibilitat del codi.

La forma habitual consisteix en la utilització d'una funció, a la qual, passant-li els paràmetres corresponents retorni el codi apropiat segons el tipus de navegador. Posteriorment s'avalua aquest codi amb la funció eval(). A l'apartat anterior (exemple de commutació d'imatges), en tenir un codi menys complex ja s'ha fet d'aquesta forma utilitzant la funció refObj().

Si voleu aïllar el codi corresponent a un sol tipus de navegador, només cal eliminar els blocs de codi que comencen amb if(tipusNavegador) ... , on tipusNavegador no correspongui amb el que voleu aïllar. La resta de codi és comuna a tots els navegadors.


Capa1:

La capa1 està parcialment oculta en carregar-se la pàgina. Observeu com a la línia 7 s'ha indicat una propietat left de -124px. Per aquest motiu queda oculta a l'esquerra. Solament es veu una imatge en forma de botó. La capa conté una taula per facilitar el posicionament del text i la imatge.

La imatge "barra.gif" està dins un link nul (línies 206-208) que detecta l'event onClick. Aquest event invoca a la funció obreTancaCapa1(). Observant el codi propi d'aquesta funció (línies 17-41) tenim que inicialment s'ha declarat i assignat una variable global i externa a la funció de nom estat amb el valor false.

La variable estat indica si la capa esta oberta (true) o tancada (false). Quan s'invoca la funció obreTancaCapa1(), el primer que es fa és determinar el valor de la propietat left de la capa segons sigui el tipus de navegador (línies 20-22) i s'assigna a espaiEsq. Posteriorment es mira el valor d'estat (línia 23), si val false s'inicia l'apertura de la capa1 (línies 33-39), si val true s'inicia el tancament de la capa1 (línies 24-30).

L'obertura i tancament es fa en passos de 5 px, que s'afegeixen o resten (línies 25-27 i 34-36) a la propietat left de la capa. A cada increment o disminució efectuat s'activa un temporitzador que tornarà a repetir el mateix procés (línies 28 i 37) fins que ja no es compleixin les condicions (espaiEsq > -124 i espaiEsq < 0, línies 24 i 33). En aquest moment es considera que la capa ja està totalment oberta o tancada, solament queda canviar el valor d'estat (línies 30 i 39) i ja s'atura tot el procés fins que l'usuari desitgi tornar a obrir o tancar.


Capa3:

Aquesta és la capa que conté el títol superior. El funcionament de la capa3 és molt similar al de la capa 1, encara que més senzill, perquè només es desplaça en un sentit. A la línia 10 tenim la definició d'estils d'aquesta capa, també podem veure com la propietat left està a -400 px, és a dir, totalment amagada a l'esquerra.

Al carregar la pàgina, s'activa automàticament l'event onLoad (línia 194) que invoca la funció mouCapa3(). Aquesta funció (línies 113-124) s'encarrega de desplaçar la capa d'esquerra a dreta igual com ho fa la Capa1 quan s'obre.

El desplaçament es fa en unitats de 5px. Cada 100 ms. es torna a invocar la funció (línia 121) i fa un pas més de 5 px, sumant-los a la variable capa3PosX que posteriorment s'assigna a la propietat left de la capa (línies 117-120).

Quan s'arriba a la posició final (capa3PosX < 10, línia 116) finalitza el posicionament d'aquesta capa i s'inicia l'animació de la Capa4 en invocar-la des de la línia 123. Si no es desitja que s'iniciï l'animació de la Capa 4 solament fa falta eliminar aquesta línia.


Capa4:

Aquesta capa és la que correspon a l' "smiley" que està rebotant aleatòriament per la finestra.
La capa està definida a les línies 238-240. El seu contingut és una imatge de 16x16 px. La imatge forma part d'un link nul. Si feu clic a sobre de la imatge (si ho podeu aconseguir ;-) ), s'atura el moviment, ja que s'executa un clearTimeout() sobre la variable sTO que conté l'identificador del temporitzador d'aquesta capa.

L'estat inicial de la capa es defineix a la línia 11, observeu com té una coordenada Z superior a les altres capes per fer que sempre vagi per sobre d'elles i la seva visibilitat inicial és oculta.

Quan finalitza el posicionament de la Capa3, s'invoca a la funció mouCapa4() (línia 123). La funció mouCapa4() (línies 125-191), s'encarrega de moure aleatòriament la capa4 i el seu contingut. El seu moviment són desplaçaments en línia recta fins a trobar un costat de la finestra on rebota. Cada cop que rebota, canvia aleatòriament la seva trajectòria i velocitat.

El primer que fa la funció mouCapa4() és determinar la mida de la superfície disponible en el cos del document (línies 129-136) i ho assigna a les variables ampleBody i altBody. En el cas que el mida de la finestra sigui més gran que el de l'objecte que conté la capa (16x16, línia 137) es determina un valor inicial aleatori capa4PosX i capa4PosY i es fa la capa visible (línies 140-156).

Posteriorment, es determina un valor d'increment X i Y aleatori entre 1 i 11, i un signe d'aquest increment igual a -1 o 1 aleatori (línies 157-160). Finalment es posa la variable posCapa4 a false (línia 161) perquè no es torni a efectuar aquest procés i s'activa un temporitzador per tornar a invocar la funció al cap de 50 ms. (línia 189).

Al tornar-se a executar la funció mouCapa4() (per la finalització del temporitzador), es torna a verificar la mida de la finestra i es va directament a la línia 164 on es comprova que la capa estigui dins l'àrea de la finestra (línia 164).

Si s'està dins la finestra, es verifica si l'increment de coordenades portarà la capa fora de la finestra, si és així, es calcula de nou, aleatòriament els valors de l'increment i el signe de desplaçament (línies 165-172). Un cop es té un valor que no ens porti fora de la finestra, s'incrementa a capa4PosX i capa4PosY i posteriorment s'assigna a les propietats left i top de la capa4 (línies 173-186).

Globalmentl, l'efecte mostra una capa que es mou de forma rectilínia dins la finestra, amb uns increments i signe de X i Y constants (velocitat i angle constants), però que quan arriba al límit de la finestra, es tornen a calcular aleatòriament els increments i signe de X i Y, canviant la velocitat i l'angle aleatòriament respecte a la trajectòria anterior.


Capa2:

La capa 2 és la més complexa de totes. Per aquest motiu s'ha deixat pel final. De fet està formada per dues capes la capa2 i la capa2a. Aquesta animació no és compatible amb Netscape 4, per tant, si detectem aquest navegador s'haurà de mostrar un contingut alternatiu.

En aquesta capa és necessari capturar els events onmousemove i onmouseup, encara no s'ha explicat res sobre la captura d'events, per tant, no s'explicarà amb detall. En la pràctica 1 del mòdul 7 s'explica com es capturen els events. Quan hagueu fet aquesta pràctica, podeu tornar a aquest apartat i analitzar amb més detall com es fa aquesta capa.

La definició de les propietats d'aquestes dues capes es fa a les línies 8 i 9, observeu com la capa2a té un posicionament relatiu. Com que està situada dins la capa 2 les seves referències top i left tindran com origen de coordenades les de la capa 2. La visibilitat de la capa2a s'hereta de la capa2 i si el seu contingut és extens apareixeran les barres de desplaçament (overflow:auto).

Aquestes dues capes es posicionen dins el bloc <body> a les línies 213-234. Observeu com la seva inclusió es fa a partir de codi JavaScript. És necessari detectar el navegador, ja que si es tracta de NN4 no podem crear la capa2a perquè es veuria malament (línia 219). Per als altres dos navegadors IE i NN6, es crea el contingut de la capa a les línies 221-231.

La variable de text txt, conté tot el contingut de la capa2 i la capa2a. Inicialment s'introdueixen tres imatges corresponents a la capçalera de la capa, són la barra de títol, el botó de minimitzar i el botó de tancar (línies 221-223). Posteriorment es crea la capa2a i el seu contingut (línies 224-230).

Les tres imatges de la capçalera responen a events introduïts amb el ratolí. Quan es fa clic a sobre de la imatge (capa2_3.jpg) s'invoca l'event onClick i es canvia la propietat de visibilitat de la capa2 a oculta (línia 223). Si es fa clic a la imatge (capa2_2.jpg), s'invoca la funció minMaxCapa2() (línia 222), observeu que aquesta imatge té un identificador : id="imgMinMax", aquest identificador servirà per poder canviar la imatge a (capa2_4.jpg) a les línies 49 i 54, quan la capa2 sigui minimitzada. Les línies 62 i 67 fan l'efecte contrari.

La imatge (capa2_1.jpg) introduïda a la línia 221, també respon a un event, en aquest cas l'event onMouseDown. Aquest event invoca la funció mouCapa2(), que s'encarrega de capturar els events onmousemove i onmouseup i permeten desplaçar la capa per sobre de la finestra.

La funció minMaxCapa2() (línies 42-71) minimitza o maximitza la capa2. Per això utilitza la variable global minMax per saber si està oberta o tancada. Per tancar la capa, posa a 0px l'alçada de la capa2a i a 23px/19px l'alçada de la capa2, segons sigui per a IE o NN6. També canvia la imatge amb identificador imgMinMax per mostrar l'altre símbol alternatiu (línies 46-54).

En cas contrari, si s'ha d'obrir la capa, fa el mateix procés, però a la inversa (línies 59-67). Per acabar cadascun dels dos processos (obertura o tancament de la capa) es canvia el valor de la variable minMax (línies 56-69).

La funció mouCapa2() és la responsable de facilitar el desplaçament. Com ja s'ha dit manipula la captura dels events onmousemove i onmouseup. A la pràctica 1 del mòdul 7 trobareu més informació sobre aquests conceptes.

La incompatibilitat amb el Netscape 4 bé determinada per la utilització de la capa2a enniuada dins la capa2. Aquest fet, més la propietat overflow assignada a la capa2, fa que sigui incompatible amb el NN4. Si aquest mateix exemple no contingues la capa2a podria funcionar perfectament amb el NN4 ja que el sistema de captura d'events és el mateix que s'ha aplicat pel cas del NN6.


Per veure el codi d'aquest exemple, s'ha de visualitzar la finestra de mostra i fer clic a sobre de la finestra amb el botó dret del ratolí. Al menú contextual que apareix, heu de seleccionar l'opció de "Visualització del codi font".