Extension:Graph/Interactive Graph Tutorial

Drawing Shapes
{ "version": 2, "width": 300, "height": 80, "padding": 12, "background": "#edf1f7", "marks": [ {     "name": "yearLabel", "type": "text", "properties": { "enter": { "x": {"value": 0}, "y": {"value": 25}, "fontSize": {"value": 32}, "fontWeight": {"value": "bold"}, "fill": {"value": "steelblue"}, "text": {"value": 2000} },     }    },    {      "name": "scrollLine", "type": "rule", "properties": { "enter": { "x": {"value": 0}, "y": {"value": 40}, "x2": {"value": 300}, "stroke": {"value": "#000"}, "strokeWidth": {"value": 2} }     }    },    {      "name": "handle", "type": "path", "properties": { "enter": { "x": {"value": 200}, "y": {"value": 40}, "path": {"value": "m-5.5,-10l0,20l11.5,-10l-11.5,-10z"}, "stroke": {"value": "#880"}, "strokeWidth": {"value": 2.5} },       "update": {"fill": {"value": "#fff"}}, "hover": {"fill": {"value": "#f00"}} }   }  ] }

We start with drawing a few elements (marks). The graph code is surrounded by the &lt;graph mode=interactive>...&lt;/graph> tag, even though the graph is not yet interactive.

Hint: Use "edit source" to copy the graph spec to the Vega Editor for experimentation.

IsDragging signal
{ "version": 2, "width": 300, "height": 80, "padding": 12, "background": "#edf1f7", "signals": [ {     "name": "isDragging", "init": false, "streams": [ {"type": "@handle:mousedown","expr": "true"}, {"type": "mouseup","expr": "false"} ]   }  ],  "marks": [ {     "name": "yearLabel", "type": "text", "properties": { "enter": { "x": {"value": 0}, "y": {"value": 25}, "fontSize": {"value": 32}, "fontWeight": {"value": "bold"}, "fill": {"value": "steelblue"}, "text": {"value": 2000} },     }    },    {      "name": "scrollLine", "type": "rule", "properties": { "enter": { "x": {"value": 0}, "y": {"value": 40}, "x2": {"value": 300}, "stroke": {"value": "#000"}, "strokeWidth": {"value": 2} }     }    },    {      "name": "handle", "type": "path", "properties": { "enter": { "x": {"value": 200}, "y": {"value": 40}, "path": {"value": "m-5.5,-10l0,20l11.5,-10l-11.5,-10z"}, "stroke": {"value": "#880"}, "strokeWidth": {"value": 2.5} },       "update": {"fill": {"value": "#fff"}}, "hover": {"fill": {"value": "#f00"}} }   },    {      "name": "debugIsDragging", "type": "text", "properties": { "enter": { "x": {"value": 250}, "y": {"value": 0}, "fill": {"value": "black"} },       "update": {"text": {"signal": "isDragging"}} }   }  ] }

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:

Handle Position signal
{ "version": 2, "width": 300, "height": 80, "padding": 12, "background": "#edf1f7", "signals": [ {     "name": "isDragging", "init": false, "streams": [ {"type": "@handle:mousedown","expr": "true"}, {"type": "mouseup","expr": "false"} ]   },    {      "name": "handlePosition", "init": 200, "streams": [ {         "type": "mousemove[isDragging]", "expr": "eventX" }     ]    }  ],  "marks": [ {     "name": "yearLabel", "type": "text", "properties": { "enter": { "x": {"value": 0}, "y": {"value": 25}, "fontSize": {"value": 32}, "fontWeight": {"value": "bold"}, "fill": {"value": "steelblue"}, "text": {"value": 2000} }     }    },    {      "name": "scrollLine", "type": "rule", "properties": { "enter": { "x": {"value": 0}, "y": {"value": 40}, "x2": {"value": 300}, "stroke": {"value": "#000"}, "strokeWidth": {"value": 2} }     }    },    {      "name": "handle", "type": "path", "properties": { "enter": { "y": {"value": 40}, "path": {"value": "m-5.5,-10l0,20l11.5,-10l-11.5,-10z"}, "stroke": {"value": "#880"}, "strokeWidth": {"value": 2.5} },       "update": { "x": {"signal": "handlePosition"}, "fill": {"value": "#fff"} },       "hover": {"fill": {"value": "#f00"}} }   },    {      "name": "debugIsDragging", "type": "text", "properties": { "enter": { "x": {"value": 250}, "y": {"value": 0}, "fill": {"value": "black"} },       "update": {"text": {"signal": "isDragging"}} }   },    {      "name": "debugHandlePosition", "type": "text", "properties": { "enter": { "x": {"value": 250}, "y": {"value": 14}, "fill": {"value": "black"} },       "update": {"text": {"signal": "handlePosition"}} }   }  ] }

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:

Scaling Handle Position signal
{ "version": 2, "width": 300, "height": 80, "padding": 12, "background": "#edf1f7", "signals": [ {     "name": "isDragging", "init": false, "streams": [ {"type": "@handle:mousedown","expr": "true"}, {"type": "mouseup","expr": "false"} ]   },    {      "name": "handlePosition", "streams": [{"type": "mousemove[isDragging]","expr": "eventX"}] },   {      "name": "scaledHandlePosition", "init": 2000, "expr": "handlePosition", "scale": {"name": "yearsScale","invert": true} } ],  "scales": [ {     "name": "yearsScale", "type": "linear", "zero": false, "domainMin": 1980, "domainMax": 2016, "range": "width" } ],  "marks": [ {     "name": "yearLabel", "type": "text", "properties": { "enter": { "x": {"value": 0}, "y": {"value": 25}, "fontSize": {"value": 32}, "fontWeight": {"value": "bold"}, "fill": {"value": "steelblue"}, "text": {"value": 2000} }     }    },    {      "name": "scrollLine", "type": "rule", "properties": { "enter": { "x": {"value": 0}, "y": {"value": 40}, "x2": {"value": 300}, "stroke": {"value": "#000"}, "strokeWidth": {"value": 2} }     }    },    {      "name": "handle", "type": "path", "properties": { "enter": { "y": {"value": 40}, "path": {"value": "m-5.5,-10l0,20l11.5,-10l-11.5,-10z"}, "stroke": {"value": "#880"}, "strokeWidth": {"value": 2.5} },       "update": { "x": {"signal": "handlePosition"}, "fill": {"value": "#fff"} },       "hover": {"fill": {"value": "#f00"}} }   },    {      "name": "debugIsDragging", "type": "text", "properties": { "enter": { "x": {"value": 250}, "y": {"value": 0}, "fill": {"value": "black"} },       "update": {"text": {"signal": "isDragging"}} }   },    {      "name": "debugHandlePosition", "type": "text", "properties": { "enter": { "x": {"value": 250}, "y": {"value": 14}, "fill": {"value": "black"} },       "update": {"text": {"signal": "handlePosition"}} }   },    {      "name": "debugScaledHandlePosition", "type": "text", "properties": { "enter": { "x": {"value": 250}, "y": {"value": 28}, "fill": {"value": "black"} },       "update": {"text": {"signal": "scaledHandlePosition"}} }   }  ] }

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:

Final version
{ "version": 2, "width": 300, "height": 80, "padding": 12, "background": "#edf1f7", "signals": [ {     "name": "isDragging", "init": false, "streams": [ {"type": "@handle:mousedown","expr": "true"}, {"type": "mouseup","expr": "false"} ]   },    {      "name": "handlePosition", "streams": [{"type": "mousemove[isDragging]","expr": "eventX"}] },   {      "name": "scaledHandlePosition", "expr": "handlePosition", "scale": {"name": "yearsScale","invert": true} },   {      "name": "currentYear", "init": 2000, "expr": "clamp(parseInt(scaledHandlePosition),1980,2016)" } ],  "scales": [ {     "name": "yearsScale", "type": "linear", "zero": false, "domainMin": 1980, "domainMax": 2016, "range": "width" } ],  "marks": [ {     "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"}} }   },    {      "name": "scrollLine", "type": "rule", "properties": { "enter": { "x": {"value": 0}, "y": {"value": 40}, "x2": {"value": 300}, "stroke": {"value": "#000"}, "strokeWidth": {"value": 2} }     }    },    {      "name": "handle", "type": "path", "properties": { "enter": { "y": {"value": 40}, "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"} },       "hover": {"fill": {"value": "#f00"}} }   },    {      "name": "debugIsDragging", "type": "text", "properties": { "enter": { "x": {"value": 250}, "y": {"value": 0}, "fill": {"value": "black"} },       "update": {"text": {"signal": "isDragging"}} }   },    {      "name": "debugHandlePosition", "type": "text", "properties": { "enter": { "x": {"value": 250}, "y": {"value": 14}, "fill": {"value": "black"} },       "update": {"text": {"signal": "handlePosition"}} }   },    {      "name": "debugScaledHandlePosition", "type": "text", "properties": { "enter": { "x": {"value": 250}, "y": {"value": 28}, "fill": {"value": "black"} },       "update": {"text": {"signal": "scaledHandlePosition"}} }   }  ] }