Journal Balance virtuelle, application éducative javascript

Posté par  . Licence CC By‑SA.
Étiquettes : aucune
12
9
sept.
2019

Dans la poursuite du travail de réécriture des activités Clicmenu entrepris par l'équipe PrimTux (voir explications ici : https://linuxfr.org/users/philippedpt35/journaux/a-la-campagne-application-educative-javascript) je vous présente l'application Balance virtuelle.

Comme son nom l'indique, elle simule une balance Roberval avec laquelle on peut soit s'exercer librement à disposer les masses sur les plateaux, soit réaliser divers exercices de pesées d'objets.

Nous apprécierons vos retours dans le but d'améliorer l'application.

Test en ligne : https://primtux.fr/applications/balance-virtuelle/
Sources sur Framagit : https://framagit.org/philippe-dpt35/balance-virtuelle

  • # Sur écran tactile

    Posté par  (site Web personnel) . Évalué à 3.

    J'ai essayé sur mon téléphone mais j'ai pas l'impression qu'on puisse faire glisser les poids.

    • [^] # Re: Sur écran tactile

      Posté par  . Évalué à 1.

      ça n'est pas encore prévu pour être opérationnel en tactile.
      ça sera l'objet de futures versions.

  • # Sur le code

    Posté par  (site Web personnel) . Évalué à 10. Dernière modification le 09/09/19 à 17:40.

    Quelques remarques en vrac (sur le code) :

    • Ca pique un peu de voir autant de variables globales et aussi destructurées, je recommande vivement d'en faire le bilan et de distinguer celles dont tu as besoin pendant toute la vie de ton application (comme difficulte) de celles qui ont une durée de vie courte et localisée (comme PositionX) et qui pourront donc être déclarées au plus proches de l'endroit où elles sont utilisées. Tu peux organiser tes variables de façon hiérarchisée et en partant d'une seule variable globale etat et qui contiendrait l'ensemble des paramètres de ton application.

    Exemple :

        const etat = {
          difficulte: 1,
          objects: [], 
          masses: [],
          chargeGauche: 0,
          chargeDroite: 0,
          (...)  
        };
    • De manière générale, ne pas hésiter à écrire le plus possible de fonction "pures", c'est plus facile de raisonner dessus quand on relit le code par exemple : la plupart de tes fonctions lisent et écrivent des variables extérieures à elles, c'est difficile de suivre le fil quand on se plonge dedans => ne pas hésiter à utiliser des arguments dans les fonctions pour éviter ce phénomène. Si tu réorganises les variables de ton programme dans un "etat", tu pourrais passer cet "etat" en argument des fonctions pures.

    • Pas mal de variables sont déclinées en 4 catégories, si j'ai bien compris, ces catégories correspondent à la fois à des index de séries d'objets mais aussi au niveau de difficulté courant. Pour mieux s'y retrouver, je regrouperai toutes ces données de manière plus sémantique (c'est juste une façon d'organiser, y'en a plusieurs possibles) :

        const series = [
          {
            difficulte: 1
            objets: [
              ["img/boite-mais.png",413,116,132,"auto"],
              ["img/cahier.png",96,239,137,"152,38"],
              (...)
            ]
          },
          {
            difficulte: 1
            objets: [
              (...)
            ]
          },
          {
            difficulte: 2
            objets: [
              (...)
            ]
          },
          {
            difficulte: 2
            objets: [
              (...)
            ]
          }
        ];
    • Je ne sais pas pourquoi tu n'as pas utilisé le même système de déclaration statique des masses dans le jeu, au lieu d'écrire cette horrible fonction creeMassesBase() :-), ca permettrait de réduire pas mal le nombre de lignes de code et l'expressivité
    • la fonction estPresent est à la fois dans balance.js et dans outils.js
    • Cela dit, tu peux la remplacer par monTableau.indexOf(monElement) >= 0 ou bien monTableau.includes(monElement) si tu peux t'en ficher d'internet explorer.
    • Se tenir à une nomenclature cohérente au niveau du nommage : par exemple, en JavaScript, l'usage veut qu'on commence le nom d'une variable en minuscule (camelCase)
    • Tu utilises des var et des let, je te recommande de laisser tomber var et d'adopter const, tu éviteras le problème d'hoisting et bénéficieras également de la vérification de mutabilité de la constante
    • Je recommande l'usage de noms anglais dans le code source si tu souhaites partager ce code en dehors de la francosphere

    Y'aurait encore plein d'autres trucs à dire, mais j'arrête là.

    En tout cas, beau projet, et ça marche plutôt pas mal sur mon ordi. Je pense qu'une amélioration fonctionnelle serait de guider un peu plus le joueur lorsqu'il souhaite déposer des poids, en mettant par exemple en surbrillance la zone de dépôt, pendant le drag'n'drop.

    • [^] # Re: Sur le code

      Posté par  . Évalué à 10.

      Je ne suis pas programmeur de métier, et ne me suis lancé sur javascript que relativement récemment. En fait j'apprends en faisant, et au niveau de la propreté du code il y a incontestablement énormément de choses à corriger. D'autant plus que cette application est une des premières que j'ai commencé à réaliser. Je constate moi-même nombre de défauts à mesure que je développe.

      Je vais (au moins je l'espère ! :D ) m'améliorer à la pratique. Je prends en compte toutes tes remarques qui me serviront dans les futurs développements, et éventuellement pour la reprise des applications déjà faites pour en améliorer la lisibilité, la priorité étant toutefois de proposer rapidement une adaptation "qui fonctionne" des différentes applications clicmenu.

    • [^] # Re: Sur le code

      Posté par  (site Web personnel) . Évalué à 3.

      Tu utilises des var et des let, je te recommande de laisser tomber var et d'adopter const, tu éviteras le problème d'hoisting et bénéficieras également de la vérification de mutabilité de la constante

      J’ai vu un commentaire sur une autre dépeche récemment qui disait d’utiliser let plutôt que var. La réponse à la question “What's the difference between using “let” and “var”?” sur StackOverflow a l’air de dire que dans la plupart des cas, ça change rien, mise à part que :

      • localement, let est limité au bloque tandis que var est limité à la fonction (globalement, il n’y a pas de différence)
      • en mode « strict », let définie des constantes.

      Je connais rien à JS, du coup ça m’intrigue : quel est l’avantage de let sur var et quel est l’intérêt du mode « strict » ?

      • [^] # Re: Sur le code

        Posté par  (site Web personnel) . Évalué à 2. Dernière modification le 10/09/19 à 16:22.

        localement, let est limité au bloque tandis que var est limité à la fonction (globalement, il n’y a pas de différence)

        J'essaye au possible d'écrire des fonctions courtes donc effectivement, re-déclarer deux fois la même variable dans une même fonction ne m'arrive pas souvent. Mais il est possible que ça soit intéressant dans d'autres cadres. En tout cas, ça ne permet plus d'écrire ce genre d'horreur :

        function toto() {
          if (...) {
            var toto = 4;
          }
          console.log(toto);
        }

        Avec let, ça ne passe pas.

        en mode « strict », let définie des constantes.

        Non, en mode strict, ce qu'on ne peut pas faire, c'est redéclarer la même variable deux fois dans un même scope.

        Pour faire une "constante" (*), il faut utiliser const.

        Je parlais de "hoisting" plus haut, le var peut être piégeux à cause de ça, let et const ne te permettent pas d'utiliser les variables avant leur déclaration (oui ça semble idiot de faire ça, mais pas mal de développeurs JavaScript usaient et abusaient de ça), et donc, n'avaient pas forcément conscience qu'en déclarant un var toto en plein milieu d'une fonction, toto était en fait remonté en haut de la fonction par l'interpréteur.
        let est ce que var aurait dû être dès le départ.
        Comme === est ce que == aurait dû être dès le départ.
        On constate que de nombreuses améliorations des différentes moutures de JavaScript ont pour objectif de corriger les erreurs de jeunesses du langage.

        (*) en réalité, seule la valeur de variable est constante, on ne peut pas utiliser const pour faire réellement de l'immutabilité, par exemple.

        const obj = { a: 4 };
        obj.a = 5; // parfaitement autorisé

        En pratique, la "fausse" immutabilité de const fait que ce dernier est largement plus utilisé que let dans les programmes depuis ES6. A vue de pif, je dirais que j'utilise 15 fois plus de const que de let. (et plus aucun var)

    • [^] # Re: Sur le code

      Posté par  . Évalué à 1.

      C'est malin ! Finalement tes remarques m'incitent à revoir le code sans attendre !  ;)
      Comme il y a quand même besoin d'avancer sur d'autres applications, je ferai ça peu à peu entre deux !

      Si tu as d'autres conseils, recommandations, n'hésite pas !

      • [^] # Re: Sur le code

        Posté par  (site Web personnel) . Évalué à 4.

        Si ça t'intéresse, je peux te proposer un patch ou pull request.

        • [^] # Re: Sur le code

          Posté par  . Évalué à 1.

          C'est sympa de ta part de le proposer.
          C'est sûr que ça permettrait d'aller plus vite, mais en même temps, j'ai besoin d'apprendre et de progresser. Et la meilleure manière d'y parvenir, c'est de faire !

          Accepterais-tu que je te soumettes le code plus tard, lorsque je l'aurai revu ?
          Tu pourrais alors affiner les choses et faire à ce moment là un pull request, car il restera certainement du boulot ! :D

          Qu'en penses-tu ?

          • [^] # Re: Sur le code

            Posté par  (site Web personnel) . Évalué à 2.

            Bien sûr, aucun soucis

            • [^] # Re: Sur le code

              Posté par  . Évalué à 1.

              Bonjour,

              j'ai retravaillé le code en suivant tes conseils.
              Il resterait encore bien du travail, notamment sur les fonctions, pour davantage aller vers des fonctions "pures". Mais ça, ça sera plutôt un effort que je ferai sur les nouvelles applications que je développerai.

              Le problème, quand on apprend en faisant, c'est que l'on n'a pas une vue d'ensemble de ce que l'on va faire avant de commencer. On résout les problèmes au fur et à mesure qu'on les rencontre, sans savoir d'avance comment on les résoudra, ni si la manière utilisée est la plus appropriée !

              Cela devrait s'améliorer à l'expérience ! C'est pourquoi les conseils des plus expérimentés sont les bienvenus.

              J'en profite par ailleurs pour te demander d'autres conseils sur des questions techniques pour lesquelles je n'ai pas trouvé réponse, mes recherches sur le Net révélant des pratiques très diverses :

              • Est-il préférable d'ajouter des éléments dans le dom depuis le html, ou de les créer par javascript ? Pourquoi ? (performances ou autres) Quelles règles à respecter à ce niveau ?

              • Lorsque l'on utilise fréquemment une référence à un élément du DOM, est-il préférable de faire un appel à la fonction javascript lors de chaque appel, ou de créer une variable globale avec cette référence ?
                Ex:
                Vaut-il mieux faire un document.getElementById('mon-element') à chaque fois que j'en ai besoin
                ou créer la variable
                const monElement = document.getElementById('mon-element')
                et y faire appel à chaque besoin ? Quel est le moins coûteux en termes de performances ?

              • [^] # Re: Sur le code

                Posté par  (site Web personnel) . Évalué à 2. Dernière modification le 04/10/19 à 13:09.

                Est-il préférable d'ajouter des éléments dans le dom depuis le html, ou de les créer par javascript ? Pourquoi ? (performances ou autres) Quelles règles à respecter à ce niveau ?

                On va partir de l'hypothèse qu'on parle d'une application comme la tienne qui tourne entièrement sur le navigateur ou au moins qui délègue entièrement son rendu au navigateur.

                Dans ce cas là, la modification du DOM via JavaScript répond essentiellement à des besoins "dynamiques", par exemple, si je veux créer un ensemble de sprites qui correspondent à l'ensemble des objets que j'ai défini en interne dans mon programme JavaScript. Le bon sens veut en effet qu'on automatise le plus de choses pour éviter de se répéter un peu partout dans son programme et dans ce cas là, la page html de base ne devra pas contenir grand chose.
                C'est la tendance actuelle pour les "Web Single App" : on définit grosso modo une structure générale statique en HTML et on laisse ensuite le programme gérer le DOM de façon dynamique.
                Dans ton cas, tout l'"univers" de ton jeu devrait idéalement être généré par JavaScript, surtout que comme ça, on a pas à faire constamment l'aller/retour entre le code et la page pour avoir une idée de comment tout ça s'articule.

                Lorsque l'on utilise fréquemment une référence à un élément du DOM, est-il préférable de faire un appel à la fonction javascript lors de chaque appel, ou de créer une variable globale avec cette référence ?

                L'utilisation de variable pour stocker des résultats d'appels de fonction répond principalement à deux besoins :
                1/ avoir un cache pour le résultat d'appel de fonction (optimisation)
                2/ éviter de se répéter dans le code (maintenabilité)

                Dans la plupart des cas, la maintenabilité d'un code est pour moi plus important que son optimisation, et en tout cas, on ne devrait pas se soucier trop tôt de l'optimisation de son programme. Comme dirait Donald Knuth, "Premature optimization is the root of all evil (or at least most of it) in programming."

                Dans ton cas, récupérer un élément par son id est quelque chose d'ultra optimisé, surtout par les navigateurs actuels, cela dit, ça reste un appel de fonction.
                Dans une boucle de 1 000 000 itérations, utiliser getElementById avoir le même id ne serait probablement pas très malin.
                Cependant, dans la grosse majorité des cas, c'est l'élégance du code, la concision et le soucis de ne pas se répéter qui devrait primer, et c'est parfois aussi une question de goût.
                En gros, pour moi, dans ton contexte, il n'y a aucun soucis à utiliser getElementById() à chaque fois qu'on a besoin de travailler sur l'élément, si ce n'est que de voir 4 getElementById('toto') au sein du même bloc d'instructions sera pénible à relire/maintenir.

                • [^] # Re: Sur le code

                  Posté par  (site Web personnel) . Évalué à 2. Dernière modification le 08/10/19 à 11:22.

                  Dans une boucle de 1 000 000 itérations, utiliser getElementById avoir avec le même id ne serait probablement pas très malin.

  • # Sur l'utilisabilité et le jeu

    Posté par  . Évalué à 2.

    Sympa et je garde en mémoire, merci ! :-)

    Voici mes idées d'amélioration d'un point de vue interface, en vrac :

    • Couleur de l'arrière plan des objets déplaçables qui change au survol
    • Taille des surfaces cliquables des poids fixes (difficile de cliquer sur les petits poids)
    • Zone de dépôt et de retrait mises en valeur quand on glisse avec un poids
    • Boutons pour modifier le zoom (le zoom traditionnel fonctionne très bien, mais on y pense pas nécessairement).
    • Un indicateur qui change quand on est à l'équilibre pourrait être sympa, ça évite de se poser la question "est-ce qu'il reste un peu à ajouter ?"
    • Une étiquette sur chaque poids pour ne pas avoir à survoler pour ne pas à en connaître le poids
    • Le texte à droite oblige à changer complètement le flux de visualisation de l'application, pour en comprendre le fonctionnement. Pour le texte explicatif, le mettre dans le bandeau des poids sur la droite aiderait déjà un peu. Pour le texte d'erreur, le mettre au plus proche de ce qui est erroné, donc le poids, me semble plus approprié.

    J'ajouterais aussi des possibilités pour les enfants plus jeunes :

    • limitation au niveau du nombre des poids, ou découverte progressive des poids (juste 1kg, 500g et ajouter au fer et à mesure des exercices)
    • Possibilité de jouer juste en mettant à l'équilibre, et vérifier sans avoir à préciser le poids.

    Aussi, je n'ai jamais pu cliquer sur Ranger les masses. Est-ce un bug ?

    Bonne continuation sur ce projet !

    • [^] # Re: Sur l'utilisabilité et le jeu

      Posté par  (site Web personnel) . Évalué à 1.

      Le bouton Ranger les masses est cliquable une fois la balance à l'équilibre.

      Le toolkit Atlas, la bibliothèque légère et facile à utilser pour débuter avec la programmation d'interfaces graphiques… (voir 'site Web personnel").

      • [^] # Re: Sur l'utilisabilité et le jeu

        Posté par  . Évalué à 4.

        Je n'avais pas fait attention à ce détail, merci.

        C'est dommage qu'il ne soit pas disponible à tout moment. Recommencer alors que plusieurs poids sont déjà sur la balance demande pas mal d'actions alors qu'un simple clic suffirait.

        • [^] # Re: Sur l'utilisabilité et le jeu

          Posté par  . Évalué à 1.

          C'est dommage qu'il ne soit pas disponible à tout moment. Recommencer alors que plusieurs poids sont déjà sur la balance demande pas mal d'actions alors qu'un simple clic suffirait.

          C'est volontaire. La rangement des masses n'est là que pour aider à calculer la masse totale avant de la saisir une fois la pesée réalisée.
          En fait il y a une méthode pour réaliser une pesée, et l'élève doit apprendre cette méthode qui consiste à commencer par les masses les plus importantes en réduisant progressivement. Il lui faut donc avoir un minimum d'organisation en posant les masses sur le plateau.

          Un indicateur qui change quand on est à l'équilibre pourrait être sympa, ça évite de se poser la question "est-ce qu'il reste un peu à ajouter ?"

          Cet indicateur existe, c'est l'aiguille de la balance avec des repères progressifs lorsque l'on approche de l'équilibre.

          Le texte à droite oblige à changer complètement le flux de visualisation de l'application, pour en comprendre le fonctionnement.

          Là je ne comprends pas ce que tu veux dire.

          Pour les autres suggestions, je les prends en note. Certaines seront rapidement mises en œuvre, les autres seront en aide-mémoire pour de prochaines versions.

          • [^] # Re: Sur l'utilisabilité et le jeu

            Posté par  . Évalué à 2.

            C'est volontaire. La rangement des masses n'est là que pour aider à calculer la masse totale avant de la saisir une fois la pesée réalisée.

            En testant, je trouve que le libellé du bouton n'est pas trop adéquat. Je m'attendais à ce que les masses soient rangées dans la zone supérieure, là où elles étaient rangées à l'origine.

            Ici, le but est de les ordonner dans une zone spécifique pour le calcul de la masse totale. Renommer en, par exemple, "aide au calcul de la masse" m'aurait aidé à la compréhension.

            En fait il y a une méthode pour réaliser une pesée

            Oui, le mode apprentissage la décrit d'ailleurs bien ;-)

            Cet indicateur existe, c'est l'aiguille de la balance avec des repères progressifs lorsque l'on approche de l'équilibre.

            En fonction de l'écran, ce n'est qu'après avoir terminé la première pesée qu'on se rend compte qu'on est juste et qu'il ne reste pas 1px de décalage. C'est en tout cas la réflexion que je me suis faite.

            Là je ne comprends pas ce que tu veux dire.

            Même en me relisant, j'ai du mal :D L'idée est d'avoir la notification la plus proche de là où l'utilisateur a son regard, et/ou là où il doit aller pour effectuer la prochaine action.
            Si je prend les erreurs qui apparaissent au moment où je clique sur vérifier, je verrais bien le texte d'erreur en surimpression juste au dessus du bouton vérifier, et si par exemple ce qui manque est d'entrer la masse de l'objet, avoir cette zone qui se colore elle aussi.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.