Extension:Graph/Tutoriel Graphe Interactif

From MediaWiki.org
Jump to navigation Jump to search
This page is a translated version of the page Extension:Graph/Interactive Graph Tutorial and the translation is 44% complete.

Other languages:
English • ‎Ripoarisch • ‎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).

Historical Fertility Rates
Historical Fertility Rates

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,
  // Set padding to the same value on all sides
  "padding": 12,
  // Par dé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 ré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"}}
      }
    }
  ]
}

IsDragging signal

To make our handle object dragable, we first need to figure out if it was clicked or not. For that, lets add a signal that becomes true when an object is clicked (isDragging), and a text mark to show (debug) the result:

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

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

Handle Position signal

Now that we know when the object is being dragged, we add a mousemove signal that only changes its value when isDragging signal is true. We also attach the new signal to the handle's "x" coordinate via "update" section:

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

  // add in "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"}}
    }
  }

Scaling Handle Position signal

Having pixel position of the handler is not very good - we would much rather have a position that is meaningful to our graph, e.g. a year. 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.

see source code
// add "scales" root value:
  "scales": [
    {
      "name": "yearsScale",
      "type": "linear",
      "zero": false,
      "domain": [1960, 2013],
      "range": "width"
    }
  ],

// in "signals", add:
    {
      "name": "scaledHandlePosition",
      "expr": "handlePosition",
      "scale": {"name": "yearsScale","invert": true}
    }

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

Year value cleanup

As you might have noticed, the handle could be moved beyond the length of our graph, producing incorrect results. 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:

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

    // Update yearLabel mark:
    // New mark to draw the year
    {
      "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"}}
      }
    },

    // Update handle mark:
    {
      "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 :

see source code
{
  // 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.