Contrôler et synchroniser ces effet d'animation avec JavaScript

Introduction

Quand on veut déplacer un élément sur un axe d'un endroit soit on utilise les animation css soit on le fait avec javascript et dans ce cas généralement la première chose qui vient en tête c'est de faire quelque chose qui ressemble a ceci.

function deplacer(chemin,nt,teno){

var element=document.getElementById("element_a_deplacer")

 element.style.left=element.offserLeft+10+"px";
 
 if(element.offserLeft<500){
 
  setTimeout(deplacer,50)
 }
}

Dans ce code on veut déplacer un élément dans l'axe horizontal jusqu’à obtenir un position de 500 pixel et pour ce faire dans l'exemple on utilise setTimeout qui appeler la fonction déplacer toutes les 50 milliseconde et qui exécute un déplacement de 10 pixel de l’élément a déplacer et quand on arrive a une valeur supérieur a 500 pixel le timer n'est plus exécuter.

cette façon de procéder est suffisante mais pose déjà des problème tel que devoir régler le déplacement et la durée entre chaque déplacement afin d'obtenir un déplacement fluide mais le pire est a venir quand on va vouloir cumuler plusieurs effets en même temps.

pour ce qui va suivre on vas cherché a déplacer des carré au centre de la page su deux axes horizontal (y) et verticale (x).

Préparation du terrain

le html

Le html est constitué de deux rectangle de couleur, de taille et de position différentes et il y a deux bouton chacun servant a lancer la fonction qui les fera se déplacé quand au Js il est constitué de deux fonction une qui préparera l'animation et une autre qui exécutera l'animation.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/css">

body{

   background-color:#91A8D2;
   height:1200px;
}

#rectangle1{
 
    height:50px;
 width :100px;
 left:100px;
 top:380px;
 background:red;
 position:absolute;
}
 
 #rectangle2{

    height:40px;
 width :60px;
 left:30px;
 top:60px;
 background:blue;
 position:absolute;
}

</style>

<script>

function preparation(elem){

}

function deplacer(){

}

</script>
</head>
<body>

<br>

<button onclick='preparation("rectangle1")'>rectangle rouge</button>
<button onclick='preparation("rectangle2")'>rectangle bleu</button>

<div id='rectangle1'>

</div>


<div id='rectangle2'>

</div>

</body></html>

Méthodologie

dans l'exemple du haut pour effectuer le déplacement on suit un mouvement mais l'objectif c'est de contrôler ce que l'on fait et pour y arriver la chose a faire est de calculer tous ce qui doit être calculer avant de lancer le déplacement.

Les paramètres

le déplacement au centre demande de connaître en premier lieux

la taille de la fenêtré du navigateur et sont centre : window.innerHeight/2 en verticale et window.innerWidth en horizontal

la taille des carré : offsetHeight et offsetWidth

la position des rectangle dans la fenêtre : offsetLeft pour la position verticale et offsetTop pour la position horizontal.

pour les carré il faut aussi retirer la moitie de leurs taille en horizontal et verticale car offsetLeft et offsetTop prennent leur référence par rapport au coté haut gauche d'un élément alors que le centrage d'un élément ce fait avec sont centre.

Calcule des distance

il s'agit des distances a parcourir horizontal et verticale correspond a la distance entre le centre de l’élément et le centre de la page.

en horizontal on prend la position centrale de la fenêtre que l'on soustrait a la position horizontal du carrée et l'on soustrait en plus la moitié de la hauteur du carré et en verticale on fait de mème mais a la verticale logique.

var distance_l=( window.innerWidth/2)-element.offsetLeft-(element.offsetWidth/2)

var distance_t=( window.innerHeight/2)-element.offsetTop-(element.offsetHeight/2)

Calcule de durée d’animation

En reprenant l'exemple du haut afin d'obtenir quelle que chose de fluide il fallait y aller a taton en jouant sur le timer et la distance parcouru a chaque appel a la fonction. Il y a un autre problème c'est le fait d'avoir deux rectangle et donc des distance différente et si on fait les réglage pour un élément afin d'arriver a la position finale en un temps satisfaisant la durée du deuxième sera différente car les distances a parcourir ne son pas les mêmes et pour finir il n'était pas possible de définir une durée d'animation.

Le fait de tous calculer en amont règlera tous ces problèmes

La première chose est d'avoir une animation fluide et pour ça on vas faire comme pour le cinéma qui considère qu'une animation est visuellement fluide a partir de 25 image par seconde dans notre cas ce sera 25 cycle de déplacements par seconde.

Le calcule est simple setTimeout prend en paramètre des millisecondes une seconde est donc égale a 1000 milliseconde que l'on divise par 25 (le nombre de déplacement par seconde) qui donne 40 millisecondes correspondant a la durée d'attente entre chaque déplacement.

1000/25=40

Maintenant si l'on veut que l'animation dure 1/2 seconde équivalent a 500 milliseconde il suffit de divise la durée par 40 pour connaître le nombre de cycle, 500/40=12.5 que l'on arrondira a 12

Exemple pour une durée d'animation de 500 milliseconde :

var nombre_de_cycle = Math.floor(500/(1000/25))

Calcule de la distance par cycle

A ce stade il reste a calculer la distance a parcourir a chaque cycle et pour ce faire il suffit de diviser la distance a parcourir par le nombre de cycle pour les axe horizontal et verticale.

var distance_l_par_cycle=distance_l / nombre_de_cycle;

var distance_t_par_cycle=distance_t / nombre_de_cycle;

Precision du rendu des calcule

La précision du rendu des calcule est limité par le fait qu'une distance par cycle calculé n'est pas toujours un entier on se retrouve donc avec un décalage de quelques pixel et pour remédier a ce problème on arrondi les valeur a son entier inférieur avec la méthode javasscript Math.floor() par exemple 20,8 sera converti a 20.

Le fait de convertir les valeurs a leurs entier inférieur risque aussi de créé un manque en fin d'animation. Ce manque sera inférieur a la position finale et du fait qu'il soit inférieur a cette position il pourra être corrigé sans effet visible. Cette correction se fera en fin de cycle d'animation en précisant la position finale cette partie sera vue plus bas.

var distance_l_par_cycle=Math.floor(distance_l / nombre_de_cycle);

var distance_t_par_cycle=Math.floor(distance_t / nombre_de_cycle);

Le code

function preparation(elem){

var element=document.getElementById(elem);

var duree_animation=500;

var nombre_de_cycle = Math.floor(duree_animation/(1000/25));

var distance_l=( window.innerWidth/2)-element.offsetLeft-(element.offsetWidth/2);

var distance_t=( window.innerHeight/2)-element.offsetTop-(element.offsetHeight/2);


var distance_l_par_cycle=Math.floor(distance_l / nombre_de_cycle);

var distance_t_par_cycle=Math.floor(distance_t / nombre_de_cycle);


deplacer(distance_l_par_cycle,distance_t_par_cycle,element,nombre_de_cycle)

}

Exécution de l'animation

Maintenant que tout est calculer il ne reste plus qu'a exécuter l'animation elle sera exécuté par une autre fonction afin de séparer les calcule et l'animation.

L’exécution de l’animation est assez simple. On positionne l’élément sur les axes top et left en utilisant les distance par cycle précédemment calculer ensuite on soustrait "1" a la valeur du nombre de cycle précédemment calculer et enfin on exécute setTimeout avec la valeur d'attente de 40 milliseconde jusqu’à ce que la valeur du nombre de cycle arrive a zéro correspondant a la position de l’élément a l'endroit approximativement désiré et enfin au dernier cycle on positionne l’élément a la position final désiré et a ce moment setTimeout n'est plus exécute la fonction est donc arrête.

function deplacer(distance_l_par_cycle,distance_t_par_cycle,element,nombre_de_cycle){

 element.style.left=element.offsetLeft+distance_l_par_cycle+"px";
 
 element.style.top=element.offsetTop+distance_t_par_cycle+"px";
 
 nombre_de_cycle--
 
 if(nombre_de_cycle>0){

  setTimeout(deplacer,40,distance_l_par_cycle,distance_t_par_cycle,element,nombre_de_cycle)
 }
 
 else{

 element.style.left=(window.innerWidth/2)-(element.offsetWidth/2)+"px";
 
 element.style.top=(window.innerHeight/2)-(element.offsetHeight/2)+"px";
 
 }
}

Le code complet et fonctionnel

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/css">

body{

   background-color:#91A8D2;
   height:1200px;
}

#rectangle1{
 
    height:50px;
 width :100px;
 left:100px;
 top:380px;
 background:red;
 position:absolute;
}
 
 #rectangle2{

    height:40px;
 width :60px;
 left:30px;
 top:60px;
 background:blue;
 position:absolute;
}
 

</style>

<script>

function preparation(elem){

var element=document.getElementById(elem);

var nombre_de_cycle = Math.floor(duree_animation/(1000/25));

var duree_animation=500;

var distance_l=( window.innerWidth/2)-element.offsetLeft-(element.offsetWidth/2);

var distance_t=( window.innerHeight/2)-element.offsetTop-(element.offsetHeight/2);

var distance_l_par_cycle=Math.floor(distance_l / nombre_de_cycle);

var distance_t_par_cycle=Math.floor(distance_t / nombre_de_cycle);


deplacer(distance_l_par_cycle,distance_t_par_cycle,element,nombre_de_cycle)

}

function deplacer(distance_l_par_cycle,distance_t_par_cycle,element,nombre_de_cycle){

 element.style.left=element.offsetLeft+distance_l_par_cycle+"px";
 
 element.style.top=element.offsetTop+distance_t_par_cycle+"px";
 
 nombre_de_cycle--
 
 if(nombre_de_cycle>0){

  setTimeout(deplacer,40,distance_l_par_cycle,distance_t_par_cycle,element,nombre_de_cycle)
 }
 
 else{

 element.style.left=(window.innerWidth/2)-(element.offsetWidth/2)+"px";
 
 element.style.top=(window.innerHeight/2)-(element.offsetHeight/2)+"px";
 
 }
}

</script>
</head>
<body>

<br>
<button onclick='preparation("rectangle1")'>rectangle rouge</button>
<button onclick='preparation("rectangle2")'>rectangle bleu</button>

<div id='rectangle1'>
</div>

<div id='rectangle2'>
</div>

</body></html>

Plus d'animation agrandir les rectangles et effectuer une rotation de 360°

Pour la suite on vas ajouter a l'animation l'agrandissement des rectangle et une rotation de 360 degré ce qui fera 5 effet simultané une translation verticale, une horizontal, un agrandissement en hauteur et un en largeur et enfin une rotation. Ca vas nous permettre de voir comment ce comporte tous ce beau monde.

Les rectangle seront agrandi de cinq fois leur taille.

Les calcule

Comme pour le positionnement on calcule en amont et on commence par les tailles finale en multipliant simplement les valeurs par 5 ensuite on calcule la valeur de déplacement pas cycle en soustrayant la taille finale a la taille initiale puis on divise par le nombre de cycle.

Le calcule de la valeur par cycle se fera uniquement avec la hauteur du rectangle pour la largeur j'utilise le produit en croix en divisant simplement la largeur par la hauteur

var taille_h_finale=element.offsetHeight*5;

var taille_w_finale=element.offsetWidth*5;

var taille_h_par_cycle=(taille_h_finale-element.offsetHeight) / nombre_de_cycle;

var taille_w_ratio=element.offsetWidth/element.offsetHeight;

Maintenant que l'on a fait les calcule il reste a faire une modification elle concerne les déplacement.

A l'origine les déplacement sont calculé avec le rectangle a taille fixe mais maintenant comme les rectangles seront plus grand les déplacement seront plus court il devront être basé sur taille finale de l’élément.


var distance_l=( window.innerWidth/2)-element.offsetLeft-(taille_w_finale/2);

var distance_t=( window.innerHeight/2)-element.offsetTop-(taille_h_finale/2);

Il ne reste plus que la rotation et pour celle ci ce sera très simple vue qu'il suffira de diviser 360 par le nombre de cycle

var angle=Math.floor(360/nombre_de_cycle)

L'animation


Pour l'animation on ajoutera les deux lignes qui modifieront la taille et une pour la rotation utilisant les transformation css.

element.style.height=element.offsetHeight+taille_h_par_cycle+"px";
 
element.style.width=taille_w_ratio*element.offsetHeight+"px";

element.style.transform='rotate('+angle*nombre_de_cycle+'deg)';

On n'a plus qu'a ajouter une ligne en fin d'animation dans la condition "else" afin de position précisément l’élément a 360 degré on aurait pu le faire aussi pour la taille mais pour le coup je ne l'ai pas.

element.style.transform='rotate('+0+'deg)';

Le code

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/css">

body{

   background-color:#91A8D2;
   height:1200px;
}

#rectangle1{
 
    height:50px;
 width :100px;
 left:1000px;
 top:380px;
 background:red;
 position:absolute;
}
 
 #rectangle2{

    height:40px;
 width :60px;
 left:30px;
 top:60px;
 background:blue;
 position:absolute;
}
 

</style>

<script>

function preparation(elem){

var element=document.getElementById(elem);

var duree_animation=500;


var nombre_de_cycle = Math.floor(duree_animation/(1000/25));


var taille_h_finale=element.offsetHeight*5;

var taille_w_finale=element.offsetWidth*5;

var taille_h_par_cycle=(taille_h_finale-element.offsetHeight) / nombre_de_cycle;

var taille_w_ratio=element.offsetWidth/element.offsetHeight;


var distance_l=( window.innerWidth/2)-element.offsetLeft-(taille_w_finale/2);

var distance_t=( window.innerHeight/2)-element.offsetTop-(taille_h_finale/2);


var distance_l_par_cycle=Math.floor(distance_l / nombre_de_cycle);

var distance_t_par_cycle=Math.floor(distance_t / nombre_de_cycle);

var angle=Math.floor(360/nombre_de_cycle)


deplacer(taille_h_par_cycle,taille_w_ratio,distance_l_par_cycle,distance_t_par_cycle,element,angle,nombre_de_cycle)

}

function deplacer(taille_h_par_cycle,taille_w_ratio,distance_l_par_cycle,distance_t_par_cycle,element,angle,nombre_de_cycle){

 element.style.left=element.offsetLeft+distance_l_par_cycle+"px";
 
 element.style.top=element.offsetTop+distance_t_par_cycle+"px";
 
 element.style.height=element.offsetHeight+taille_h_par_cycle+"px";
 
 element.style.width=taille_w_ratio*element.offsetHeight+"px";
 
 element.style.transform='rotate('+angle*nombre_de_cycle+'deg)';
 
 nombre_de_cycle--
 
 if(nombre_de_cycle>0){

setTimeout(deplacer,40,taille_h_par_cycle,taille_w_ratio,distance_l_par_cycle,distance_t_par_cycle,element,angle,nombre_de_cycle)
 }
 
 else{

 element.style.left=(window.innerWidth/2)-(element.offsetWidth/2)+"px";
 
 element.style.top=(window.innerHeight/2)-(element.offsetHeight/2)+"px";
 
 element.style.transform='rotate('+0+'deg)';
 
 }
}

</script>
</head>
<body>

<br>

<button onclick='preparation("rectangle1")'>rectangle rouge</button>
<button onclick='preparation("rectangle2")'>rectangle bleu</button>

<div id='rectangle1'>
</div>

<div id='rectangle2'>
</div>

</body></html>

Conclusion

Que c'est fluide que c'est précis c'est une des première chose que l'on constate surtout que l'on se retrouve avec 5 effet différents mais mis a part ça on notera le contrôle totale de l'animation qui est du au paramétrage du temps mais aussi a celui du nombre de cycle et au déplacement par cycle pour exécuter une animation et ce sont bien ces élément qui font que le peut dire que l'on contrôle voir même maîtrise l'animation en fin de compte ce qu'il faut retenir c'est que pour tout effets la méthodologie de calculer ce qui doit être calculer en amont fait des miracles.

Ce document intitulé « Contrôler et synchroniser ces effet d'animation avec JavaScript » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous