IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

Fluidifier une animation en javascript


Sujet :

JavaScript

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    101
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juin 2007
    Messages : 101
    Points : 59
    Points
    59
    Par défaut Fluidifier une animation en javascript
    Bonjour à tous,

    j'ai fait un petit bout de code pour insérer une animation sur un site que je réalise. Malheureusement l'animation est saccadée ce qui est plutôt désagréable.
    J'ai bien essayé dans le <style> de mettre une transition: pas mieux...
    Quelqu'un aurait-il une idée pour améliorer tout ça ou suis-je carrément sur la mauvaise piste et dans ce cas comment faire autrement ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    <!DOCTYPE html>
    <html>
    <head>
    <script>
    i=0;
     
    function cercle(){
    if(i>=2){i=0;
    return;}
     
    i+=0.03333;  //30 images secondes pdt 2 sec. soit 60 images pour passer de 0 à 2PI => 2/60=0.0333
    angle=i*Math.PI;
    x=200+(100*Math.cos(angle));
    y=200+(100*Math.sin(angle));
    document.getElementById('point').style.left=x+"px";
    document.getElementById('point').style.top=y+"px";
     
    setTimeout('cercle()',33);  // 60 * 33ms <> 2 sec.
    }
    </script>
    <style>
    <!-- -moz-transition-property:all;
    -moz-transition-duration:33ms;
    -webkit-transition-property:all;
    -webkit-transition-duration:33ms;
    -o-transition-property:all;
    -o-transition-duration:33ms;
    -ms-transition-property:all;
    -ms-transition-duration:33ms;
    transition-property:all;
    transition-duration:33ms; -->
    </style>
    </head>
    <body>
    <img alt="" src="point.png" id="point" style="position:absolute; left:300px; top:200px;" />
    <p style="cursor:pointer;" onclick="cercle();">Lancer l'animation</p>
    </body>
    </html>
    Merci d'avance.
    Philippe

  2. #2
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Bonjour,

    Les transitions CSS vont faire pire que mieux, car tu calcules des interpolations toutes les 33ms... Tout d'abord utilises requestAnimationFrame au lieu de setTimeout : https://developer.mozilla.org/fr/doc...AnimationFrame

    Ensuite mets en mémoire à l'extérieur de la fonction d'animation tout ce que tu peux : ton élément #point par exemple.

    Enfin quand tu assignes left puis top, l'élément doit être redessiné deux fois. Fais les deux assignements en une seule étape, avec setAttribute("style") par exemple.

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    101
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juin 2007
    Messages : 101
    Points : 59
    Points
    59
    Par défaut
    Merci Sylvain,
    je vais regarder ton lien
    pour setAttribute, je dois écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    im=document.getElementById('point')  // en dehors de la fonction
    puis dans la fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    im.setAttribute("style.top","y+'px' ") et im.setAttribute("style.left","x+'px' ")
    ?
    Dans ce cas l'attribution prend également 2 étapes non?

    A+
    philippe

  4. #4
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 094
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 094
    Points : 6 755
    Points
    6 755
    Par défaut
    Salut,

    tu te mélanges les pinceaux. Les transitions CSS servent à faire tout le travail d'animation à ta place. Si tu veux qu'un objet aille d'un point A à un point B, tout ce que tu as à faire dans le code JavaScript c'est de lui donner un point B (par exemple monObjet.style.left = "200px";). Les étapes de transition se font toutes seules.

    Dans le code que tu as donné, tu donnes un nouveau point B toutes les 33 ms. Au lieu d'avoir, comme tu le penses, une seule transition qui gère tous ces points B, tu as en réalité une transition différente à chaque fois. Le fait que toutes ces transitions se suivent dans des délais de temps aussi courts est peut-être la raison des saccades.

    Une autre cause possible des saccades c'est ce qu'on appelle l'easing. Il s'agit d'un « adoucissement » de la vitesse de déplacement vers le début et la fin de la transition. À cause de cet easing, la vitesse de mouvement n'est pas régulière. Tu peux corriger ça en spécifiant transition-timing-function: linear. Pour mieux comprendre les différents types d'easing, vois la page d'exemples du MDN.

    La fluidité dépend aussi, bien sûr, de la puissance de ton ordinateur. Il faut savoir que setTimeout ne garantit pas l'exactitude du délai demandé. Si tu demandes un délai de 33 ms, le déclenchement du timer se fera dans au moins 33 ms. JavaScript étant historiquement monothread, les appels de timers se font sur le même thread que le code « principal » et les évènements de l'interface utilisateur. Si le thread est occupé au moment prévu (par exemple par un gestionnaire d'évènement onclick ou onmousemove ou autre), le déclenchement est repoussé jusqu'à ce que le thread soit libre. Évidemment, si ton processeur est moins puissant, le thread met plus de temps à faire son travail, et il y a plus de chances que le timer tombe sur un moment où le thread est occupé. John Resig a écrit un article qui explique bien ça (je n'ai pas trouvé de version française, désolé).

    Avec les transitions, ça dépend aussi de la puissance, mais il y a quelques avantages. Le nombre d'images par secondes est ajusté en fonction de la charge du processeur, ou du GPU si l'accélération matérielle est activée. Utiliser les transitions (ou animations) CSS est plus intéressant que du pur JS car la solution JS ne tire pas parti de la puissance graphique. Dans le cas présent, ça risque d'être assez compliqué si tu veux garder tes deux propriétés top et left, car elles varient de façon synchronisée mais différente. Je te donne une piste : tu peux y arriver en utilisant rotate.

    Si tu décides d'opter pour une solution pur JS, je te suggère d'utiliser la fonction requestAnimationFrame. En pratique, elle fonctionne un peu comme setTimeout, sauf qu'elle choisit elle-même le délai adapté à une animation visuelle tout en s'ajustant à la charge du processeur. L'inconvénient dans ce cas, c'est que tu ne peux plus contrôler la durée totale de l'animation.




    Edit: je n'avais pas vu la réponse de Sylvain
    Le setAttribute n'est pas utile car les modifications CSS sont toutes prises en compte d'un seul bloc dans le repaint suivant.

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    101
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juin 2007
    Messages : 101
    Points : 59
    Points
    59
    Par défaut
    Merci Watilin pour tes explications complètes et détaillées.

    Alors voilà où j'en suis, sauf que l'animation ne se fait pas (apparemment j'ai un problème avec mon setAttribute...)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    <!DOCTYPE html>
    <html>
    <head>
    <script>
    window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
     
    i=0;
     
    function cercle(){
    if(i>=2){i=0;
    return;}
     
    i+=0.03333;  //30 images secondes pdt 2 sec. soit 60 images pour passer de 0 à 2PI => 2/60=0.0333
    angle=i*Math.PI;
    x=Math.round(200+(100*Math.cos(angle)));
    y=Math.round(200+(100*Math.sin(angle)));
    att="left:"+x+"px; top:"+y+"px;";
    //alert(att);
    im.setAttribute("style",att);
     
    requestAnimationFrame(cercle); 
    }
    </script>
     
    </head>
    <body>
    <img alt="" src="point.png" id="point" style="position:absolute; 
    left:300px; top:200px;" />
    <p style="cursor:pointer;" onclick="cercle();">Lancer l'animation</p>
    <script type="text/javascript">im=document.getElementById('point');</script>
    </body>
    </html>
    pourtant l'affichage de la variable att semble montrer que ma concaténation est correcte...

    je veux bien encore un peu d'aide SVP

    Philippe

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    101
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juin 2007
    Messages : 101
    Points : 59
    Points
    59
    Par défaut
    J'ajoute ceci après avoir vu l'edit de Watilin:

    Si je n'utilise plus le SetAttribute, ça donne ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    <!DOCTYPE html>
    <html>
    <head>
    <script>
    window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
     
    i=0;
     
    function cercle(){
    if(i>=2){i=0;
    return;}
     
    i+=0.03333;  //30 images secondes pdt 2 sec. soit 60 images pour passer de 0 à 2PI => 2/60=0.0333
    angle=i*Math.PI;
    x=200+(100*Math.cos(angle));
    y=200+(100*Math.sin(angle));
    //att="left:"+x+"px; top:"+y+"px;";
    //alert(att);
    //im.setAttribute("style",att);
    im.left=x+"px";
    im.top=y+"px";
    requestAnimationFrame(cercle);  // 60 * 33ms <> 2 sec.
    }
    </script>
     
    </head>
    <body>
    <img alt="" src="point.png" id="point" style="position:absolute; 
    left:300px; top:200px;" />
    <p style="cursor:pointer;" onclick="cercle();">Lancer l'animation</p>
    <script type="text/javascript">im=document.getElementById('point').style;</script>
    </body>
    </html>
    Là l'animation fonctionne, c'est beaucoup plus fluide qu'au début mais avec une image c'est pas encore terrible, il n'y a pas un moyen de se synchroniser à la fréquence de rafraichissement de l'écran ?

    Pour "rotate" je crois que ça va plutôt faire tourner l'image sur elle même non ?

    philippe

  7. #7
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 094
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 094
    Points : 6 755
    Points
    6 755
    Par défaut
    Citation Envoyé par filtep Voir le message
    Là l'animation fonctionne, c'est beaucoup plus fluide qu'au début mais avec une image c'est pas encore terrible, il n'y a pas un moyen de se synchroniser à la fréquence de rafraichissement de l'écran ?
    Hélas non, du moins pas à ma connaissance.

    Citation Envoyé par filtep Voir le message
    Pour "rotate" je crois que ça va plutôt faire tourner l'image sur elle même non ?
    Ça dépend où tu places le point transform-origin

    Bien que tu aies corrigé le problème du setAttribute, je pense que l'explication te paraîtra utile : le problème venait du fait que tu as défini tous les styles de l'image dans son attribut style plutôt que dans une balise <style> ou une feuille de style externe. Du coup, en utilisant setAttribute("style", ...) tu redéfinis entièrement les styles de l'image. Sa règle position est écrasée, et elle se retrouve avec un positionnement par défaut (static).

    Définir des styles sur les attributs HTML est en général considéré comme une mauvaise pratique, à cause de ce problème et aussi parce que ça complique la maintenance du code. C'est plus pratique d'avoir les codes HTML, JS et CSS bien séparés, si possible dans des fichiers distincts.

    En parlant de bonnes pratiques, je constate que tu ne déclares pas tes variables et que ton indentation laisse à désirer. Pour l'indentation, ça ne tient qu'à toi… Pour les variables, je te conseille de passer en mode strict, il te donnera pas mal de bonnes habitudes. Apprends aussi à te servir de la console de ton navigateur (touche F12) : tu peux utiliser console.log à la place de alert, tu verras que c'est beaucoup plus agréable.

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    101
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juin 2007
    Messages : 101
    Points : 59
    Points
    59
    Par défaut
    Merci beaucoup pour ces conseils que je vais appliquer dès aujourd'hui
    je progresse pas à pas grâce à vous tous.
    Merci encore et bonne nuit.

    A bientôt
    Philippe

  9. #9
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Citation Envoyé par Watilin Voir le message
    Le setAttribute n'est pas utile car les modifications CSS sont toutes prises en compte d'un seul bloc dans le repaint suivant.
    Tu es sûr que ça vaut pour tous les navigateurs ça Watilin ? J'avais en tête que setter les propriétés de style en inline une à une était une mauvaise pratique.

  10. #10
    Expert éminent
    Avatar de Watilin
    Homme Profil pro
    En recherche d'emploi
    Inscrit en
    Juin 2010
    Messages
    3 094
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : En recherche d'emploi

    Informations forums :
    Inscription : Juin 2010
    Messages : 3 094
    Points : 6 755
    Points
    6 755
    Par défaut
    Pour répondre simplement à ta question, non je ne suis pas sûr. Mais ça me paraît évident du point de vue des performances, à la fois du moteur JavaScript et du moteur de rendu.

    Cela dit, il y a certaines instructions JavaScript (par exemple un appel à getComputedStyle ou un accès à une propriété offsetLeft) qui nécessitent un reflow immédiat. Dans ce cas, le script est suspendu le temps que le reflow se fasse. Il me semblait avoir lu une page sur le MDN qui expliquait ça mais je ne la trouve plus…

  11. #11
    Rédacteur

    Avatar de Bovino
    Homme Profil pro
    Développeur Web
    Inscrit en
    Juin 2008
    Messages
    23 647
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2008
    Messages : 23 647
    Points : 91 220
    Points
    91 220
    Billets dans le blog
    20
    Par défaut
    Personnellement, j'ai tendance à ne jamais utiliser setAttribute().
    Tout d'abord parce que sémantiquement, cette fonction agit sur le HTML (voire le XML) alors qu'en JavaScript, on travaille sur les objets issus du DOM. Il semble donc préférable d'utiliser les propriétés correspondantes des objets DOM.
    Pour ce qui est des styles, effectivement, on peut considérer que multiplier les affectations sur HTMLElement.style est une mauvaise idée, notamment niveau performances puisque chaque affectation force un repaint, mais on oublie trop souvent la propriété cssText qui permet d'affecter plusieurs styles en même temps.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    HTMLElement.style.cssText = 'prop1:val1; prop2: val2; ...';
    HTMLElement.style.cssText += ';prop1:val1; prop2: val2; ...';
    Notez l'astuce lorsque vous ajoutez des styles au lieu d'affecter avec le point-virgule au début. C'est dû au fait qu'IE renvoie la chaine sans point-virgule final ce qui fait que la concaténation avec la chaine supplémentaire provoquera une erreur d'interprétation. Sur les autres navigateurs, on aura un point-virgule doublé, mais ça, ce n'est pas problématique.

  12. #12
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Exact j'oublie toujours cssText, merci Bovino

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [DOM] adapter une animation javascript au navigateur firefox
    Par nadiaflamingenierie dans le forum Général JavaScript
    Réponses: 26
    Dernier message: 04/03/2009, 10h52
  2. Réponses: 3
    Dernier message: 05/06/2007, 17h01
  3. Réponses: 2
    Dernier message: 09/03/2007, 16h52
  4. popup javascript sous une animation flash
    Par saint-pere dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 20/08/2006, 15h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo