3 votes

Problèmes de création d'un minuteur d3.js

J'ai créé une animation basée sur les temps des skieurs athlètes olympiques. J'ai converti les temps en millisecondes pour créer la durée de l'animation comme suit :

.transition()
        .duration(function(d){
          return d.time_milliseconds / 4
        })
        .attr("cx", 720)

J'aimerais maintenant montrer le temps qui passe dans l'animation. Quelque chose comme ceci : http://www.wsj.com/graphics/2018-winter-olympics-art-of-the-millisecond/ Où en haut à gauche le temps passe pendant que l'animation se déroule.

Je ne suis pas tout à fait sûr de comment créer ce minuteur. Ici, j'ai créé une fonction getTimeData mais je ne suis pas sûr de comment l'implémenter ou si c'est la bonne approche :

function getTimeData(){
              var now = new Date;
              var milliseconds = now.getMilliseconds()
              var seconds = now.getSeconds()
              var minutes = now.getMinutes()
              return {"ms": milliseconds, "seconds": seconds, "minutes": minutes}
            }

Vous pouvez voir tout mon code sur mon bl.ock : https://bl.ocks.org/JulienAssouline/256a51554899b8619ba7918590a569f1

Pour clarifier, je voudrais afficher les temps en Minutes, Secondes et Millisecondes.

2voto

Andrew Reid Points 16844

Vous pouvez démarrer un minuteur (dans ce cas, j'utilise d3.timer) lorsque les transitions commencent et l'arrêter à la fin de la transition. Ce minuteur peut être utilisé pour afficher le temps écoulé.

Transitions Multiples

Les transitions D3 incluent la capacité d'écouter les événements de début et de fin de transition. Cependant, lors de l'application d'une transition sur une sélection de plusieurs éléments, vous créez en fait plusieurs transitions - une pour chaque élément de la sélection. Par conséquent, ces écouteurs se déclenchent pour chaque élément en cours de transition. Cela pose moins de problème lors de l'initialisation d'une transition si tout commence en même temps : vous pouvez appeler une fonction pour démarrer le chronométrage autant de fois que vous le souhaitez avec peu d'effet. Cela pose problème si les transitions se produisent sur des durées différentes : vous devez attendre que la dernière transition se termine avant d'appeler une fonction pour arrêter le chronométrage.

Une option possible pour détecter la fin de plusieurs transitions est d'utiliser un compteur pour suivre combien de transitions se sont terminées :

var counter = 0;
selection.transition()
  .on("end", function() { 
    counter++; 
    if (counter == selection.size()) { // tout est terminé }
  })

Une autre considération est que plusieurs transitions ne commenceront pas en même temps, même si elles sont programmées pour commencer immédiatement - le navigateur les démarrera toutes en succession rapide mais pas simultanément si elles sont en millisecondes.

Minuteur d3 & Précision

Il est relativement simple de faire fonctionner le minuteur,

var timer = d3.timer(function(t) { // fonction de rappel
  if (t > 5000) timer.stop();  // s'exécute pendant 5 secondes
  d3.select("p").html(t);       // mettre à jour un texte
});

Cependant, gardez à l'esprit que le minuteur ne fonctionne pas en continu, il fonctionne de manière répétée. Il ne déclenche pas le rappel à chaque instant possible - cela serait un nombre infini d'opérations - mais aussi rapidement que possible, ce qui variera en fonction des circonstances. Comme le mentionne la documentation de d3, d3.timer :

Planifie un nouveau minuteur, invoquant le rappel spécifié de manière répétée jusqu'à ce que le minuteur soit arrêté. (lien)

C'est pourquoi il est possible pour le minuteur de dépasser le temps total comme dans l'exemple ci-dessus.

Exemple

Les transitions multiples commenceront légèrement à des moments différents - normalement, cela peut être imperceptible, mais avec un minuteur indiquant quand tout est terminé, la transition de plusieurs éléments peut entraîner l'affichage d'un temps peu précis.

En outre, même un seul élément en cours de transition entraînera une évaluation répétée, pas continue, de la transition, de sorte que vous pourriez avoir des temps signalés supérieurs ou inférieurs à la durée maximale programmée que vous avez définie.

L'exemple ci-dessous devrait le démontrer, les transitions sont programmées pour se terminer après 10 secondes (10 éléments, avec durée = i*1000+1000, avec un maximum de i étant de 9), mais le temps affiché ne correspondra probablement pas à cela avec un degré de précision élevé lorsqu'on travaille en millisecondes :

var width = 600;
var height = 500;

var svg = d3.select("body").append("svg")
  .attr("width",width)
  .attr("height",height);

var text = svg.append("text")
  .attr("x", 50)
  .attr("y", 40);

var circles = svg.selectAll("circle")
  .data(d3.range(10))
  .enter()
  .append("circle")
  .attr("cx", 30)
  .attr("cy", function(d) { return d * 15 + 45; })
  .attr("r",6);

var completed = 0;
var n = circles.size(); 

circles.transition()
  .on("start", function(d,i) { if(i ==0) startTimer();  })
  .on("end", function() { completed++ })
  .attr("cx", width-30)
  .duration(function(d) { return d*1000+1000; })

var format = d3.timeFormat("%S.%L");

function startTimer() {
  var timer = d3.timer(function(t) { 
    if (completed >= n) timer.stop();
    text.text("millisecondes:" + format(t));
  });
}

Précision

L'ordinateur n'obtiendra pas forcément les millisecondes correctement, en fonction de la manière dont vous mettez à l'échelle les données cela peut poser problème - si vous ralentissez les choses, vous pouvez facilement arrondir cette erreur. Si vous montrez les choses en temps réel, peut-être pas.

Si vous affichez les transitions en temps réel, alors la différence de millisecondes est visuellement imperceptible, le seul problème est que le minuteur affiche un temps écoulé différent de ce qui est prévu. Pour résoudre cela, nous pouvons utiliser une approche légèrement différente :

  • Définir un minuteur pour commencer au début de la première transition (comme ci-dessus)
  • Arrêtez le minuteur lorsque la durée maximale s'est écoulée (pré-calculée)
  • Définir le texte affichant le temps sur la durée maximale si le minuteur s'est arrêté, de sorte qu'il indique la durée maximale, pas quand elle s'est arrêtée en réalité :

    var width = 600; var height = 300;

    var svg = d3.select("body").append("svg") .attr("width",width) .attr("height",height);

    var text = svg.append("text") .attr("x", 50) .attr("y", 40);

    var circles = svg.selectAll("circle") .data(d3.range(10)) .enter() .append("circle") .attr("cx", 30) .attr("cy", function(d) { return d * 15 + 45; }) .attr("r",6);

    var maxTime = d3.max(circles.data(), function(d) { return d * 1000 + 1000;
    })

    circles.transition() .on("start", function(d,i) { if(i==0) startTimer() }) .attr("cx", width-30) .duration(function(d) { return d*1000+1000; })

    var format = d3.timeFormat("%S.%L");

    function startTimer() { var timer = d3.timer(function(t) { if (t > maxTime) { timer.stop(); text.text("millisecondes" + format(maxTime)); // assurer que le temps final est correct. } else { text.text("millisecondes:" + format(t));
    } }); }

Pour formater les nombres, je vous suggère d'utiliser simplement un format de nombre d3. Les deux extraits ci-dessus (sauf le premier) utilisent un format de base, mais il est facile de l'adapter.

2voto

Gerardo Furtado Points 61849

Une alternative à l'excellent réponse d'Andrew consiste à obtenir le temps maximum...

var maxTime = d3.max(data, function(d) {
    return d.time_miliseconds
});

... et utiliser un attrTween pour afficher l'élément texte :

textSelection.transition()
    .duration(maxTime / 4)
    .attrTween("text", function(d) {
        var that = this
        var i = d3.interpolateNumber(0, maxTime);
        return function(t) {
            return d3.select(that).text("Temps : " + i(t));
        }
    });

Remarquez que, dans cette approche, la d3.ease sélectionnée modifie le taux de changement du texte.

Voici votre bl.ocks modifié : https://bl.ocks.org/anonymous/7bdaf9f1a6a0123f83229a92b42460a0/3cfd14c76080ac2461ab91ce98a3bbe253ba8765

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X