Extension:Graph/Tutoriel Graphe Interactif

This page is a translated version of the page Extension:Graph/Interactive Graph Tutorial and the translation is 87% complete.
Other languages:
English • ‎Ripoarisch • ‎Türkçe • ‎español • ‎français • ‎polski • ‎日本語

Dans ce tutoriel, nous allons créer un graphe interactif qui affichera les taux de fertilité historiques par pays, avec un curseur pour sélectionner l'année, et une carte pour montrer la distribution des taux partout sur la planète. Si vous voulez investiguer le code source de ce graphe uniquement, il est disponible sur une page séparée. Utilisez le bac à sable graphique pour vos tests. Vous pourriez également être intéressés par la documentation complète de Vega (en anglais).

Move the slider to change the year, and hover the mouse over countries to read the rate. Try it !

Dessiner des formes

Nous commençons par dessiner quelques éléments (marques). Le code du graphe est encadrer par la balise <graph mode=interactive>...</graph>, même ci celui-ci n'est pas interactif. Ouvrez le bac à sable graphique et copiez y le code suivant sans la balise <graph> pour vous donner une idée.

Astuce : utilisez l'« éditeur de code » pour copier les spécifités du graphe sur l'éditeur Vega pour vous entrainer.

voir le code source
{
  // Nous voulons utiliser Vega 2, et indiquez la taille de l'image
  "version": 2, "width": 300, "height": 80,
  // finissez la largeur de la zone de remplissage à la même valeur de chaque té.
  "padding": 12,
  // Par faut, le fond est transparent
  "background": "#edf1f7",
  "marks": [
    {
      // Tracer une ligne horizontale
      "name": "scrollLine",
      "type": "rule",
      "properties": {
        "enter": {
          "x": {"value": 0},
          "y": {"value": 40},
          "x2": {"value": 300},
          "stroke": {"value": "#000"},
          "strokeWidth": {"value": 2}
        }
      }
    },
    {
      // Tracer une forme de triangle avec un effet « hover »
      // nommer les objets nous permet de les férencer plus tard
      "name": "handle",
      "type": "path",
      "properties": {
        "enter": {
          "x": {"value": 200},
          "y": {"value": 40},
          // la syntaxe du chemin est la même que la balise du chemin SVG
          "path": {"value": "m-5.5,-10l0,20l11.5,-10l-11.5,-10z"},
          "stroke": {"value": "#880"},
          "strokeWidth": {"value": 2.5}
        },
        "update": {"fill": {"value": "#fff"}},
        // Changer la couleur de remplissage de l'objet sur lequel la souris se trouve
        "hover": {"fill": {"value": "#f00"}}
      }
    }
  ]
}

Signal IsDragging

Pour que l'on puisse faire glisser notre objet poignée, il faut d'abord savoir si on a cliqué dessus ou pas. Pour cela, ajoutons un signal qui devient true quand l'objet est cliqué (isDragging), et une marque textuelle pour afficher (debogage) le résultat :

voir le code source
"signals": [
  {
    "name": "isDragging",
    "init": false,
    "streams": [
      {"type": "@handle:mousedown","expr": "true"},
      {"type": "mouseup","expr": "false"}
    ]
  },
],

// dans "marks"
  {
    "name": "debugIsDragging",
    "type": "text",
    "properties": {
      "enter": {
        "x": {"value": 250},
        "y": {"value": 0},
        "fill": {"value": "black"}
      },
      "update": {"text": {"signal": "isDragging"}}
    }
  }

Gérer le signal Position

Maintenant que nous savons quand l'objet est glissé, nous ajoutons un signal mousemove qui ne change de valeur que lorsque le signal isDragging est true. Nous attachons aussi le nouveau signal au gestionnaire de coodonnées « x » via la section « update » :

voir le code source
// dans "signals"
  {
    "name": "handlePosition",
    "init": 200,
    "streams": [
      {
        "type": "mousemove[isDragging]",
        "expr": "eventX()"
      }
    ]
  }

  // ajouter dans "marks"
  {
    "name": "handle",
    ...
    "update": {
      "x": {"signal": "handlePosition"},
      ...
    },
  },
  {
    "name": "debugHandlePosition",
    "type": "text",
    "properties": {
      "enter": {
        "x": {"value": 250},
        "y": {"value": 14},
        "fill": {"value": "black"}
      },
      "update": {"text": {"signal": "handlePosition"}}
    }
  }

Echelonnage du signal de position de la poignée

Avoir la position en pixels du curseur n'est pas très utile - nous préfèrerions plutôt avoir une valeur qui a un sens pour notre graphe, comme l'année. Vega scales provide a useful mechanism for converting between our data (e.g. years) and the screen coordinates, and back (invert). In this step, we add "yearsScale" linear scale for values 1960..2013, mapping it to the whole width of the graph (excluding padding). We also add a new scaledHandlePosition signal that translates from the mouse X position to the meaningful value in years.

voir le code source
// ajouter la valeur de base de "scales" :
  "scales": [
    {
      "name": "yearsScale",
      "type": "linear",
      "zero": false,
      "domain": [1960, 2013],
      "range": "width"
    }
  ],

// dans "signals" ajouter :
    {
      "name": "scaledHandlePosition",
      "expr": "handlePosition",
      "scale": {"name": "yearsScale","invert": true}
    }

  // ajouter dans "marks"
    {
      "name": "debugScaledHandlePosition",
      "type": "text",
      "properties": {
        "enter": {
          "x": {"value": 250},
          "y": {"value": 28},
          "fill": {"value": "black"}
        },
        "update": {"text": {"signal": "scaledHandlePosition"}}
      }
    }

Effacer la valeur de l'année

Si vous l'avez remarqué, la poignée peut dépasser la largeur du graphique ce qui produit des résultats incohérents. Also, the scaled value is not an integer that we expect of the year value. To fix this, we can introduce one more post-processing signal called "currentYear" to convert it to an integer and limit it to the needed range. We also initialize it to a reasonable value, and we tie both the "yearLabel" and the position of the handle bar back to this value. Note that the handle's position needs to be in screen coordinates, so we have to use the "yearsScale" to convert the value back:

voir le code source
    // Nouveau signal :
    {
      "name": "currentYear",
      "init": 2000,
      "expr": "clamp(parseInt(scaledHandlePosition),1960,2013)"
    }

    // Mettre à jour la marque yearLabel :
    // Nouvelle marque pour afficher l'année
    {
      "name": "yearLabel",
      "type": "text",
      "properties": {
        "enter": {
          "x": {"value": 0},
          "y": {"value": 25},
          "fontSize": {"value": 32},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "steelblue"}
        },
        "update": {"text": {"signal": "currentYear"}}
      }
    },

    // Mettre à jour la marque handle :
    {
      "name": "handle",
      "properties": {
        "update": {
          "x": {"scale": "yearsScale","signal": "currentYear"}
        },
      }
    }

Nettoyage final

À présent nous pouvons retirer toutes les marques de débogages. Nous n'avons également plus besoin de séparer les signaux handlePosition et scaledHandlePosition parce que la mise à l'échelle peut avoir lieu à la même étape :

voir le code source
{
  // We want to use Vega 2, and specify image size
  "version": 2, "width": 300, "height": 80,
  // Set padding to the same value on all sides
  "padding": 12,
  // By default the background is transparent
  "background": "#edf1f7",

  "signals": [
    {
      "name": "isDragging",
      "init": false,
      "streams": [
        {"type": "@handle:mousedown","expr": "true"},
        {"type": "mouseup","expr": "false"}
      ]
    },
    {
      "name": "scaledHandlePosition",
      "streams": [
        {
          "type": "mousemove[isDragging]",
          "expr": "eventX()",
          "scale": {"name": "yearsScale","invert": true}
        }
      ]
    },
    {
      "name": "currentYear",
      "init": 2000,
      "expr": "clamp(parseInt(scaledHandlePosition),1960,2013)"
    }
  ],

  "scales": [
    {
      "name": "yearsScale",
      "type": "linear",
      "zero": false,
      "domain": [1960, 2013],
      "range": "width"
    }
  ],

  "marks": [
    {
      // draw the year label in the upper left corner
      "name": "yearLabel",
      "type": "text",
      "properties": {
        "enter": {
          "x": {"value": 0},
          "y": {"value": 25},
          "fontSize": {"value": 32},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "steelblue"}
        },
        "update": {"text": {"signal": "currentYear"} }
      }
    },
    {
      // Draw a horizontal line
      "name": "scrollLine",
      "type": "rule",
      "properties": {
        "enter": {
          "x": {"value": 0},
          "y": {"value": 40},
          "x2": {"value": 300},
          "stroke": {"value": "#000"},
          "strokeWidth": {"value": 2}
        }
      }
    },
    {
      // Draw a triangle shape with a hover effect
      // naming objects allows us to reference them later
      "name": "handle",
      "type": "path",
      "properties": {
        "enter": {
          "y": {"value": 40},
          // path syntax is the same as SVG's path tag
          "path": {"value": "m-5.5,-10l0,20l11.5,-10l-11.5,-10z"},
          "stroke": {"value": "#880"},
          "strokeWidth": {"value": 2.5}
        },
        "update": {
          "x": {"scale": "yearsScale","signal": "currentYear"},
          "fill": {"value": "#fff"}
        },
        // Change fill color of the object on mouse hover
        "hover": {"fill": {"value": "#f00"} }
      }
    }
  ]
}

Prochaine étape

Veuillez continuer sur la partie 2.