Skip to content

Instantly share code, notes, and snippets.

@ColinEberhardt
Last active May 28, 2019 13:59
Show Gist options
  • Save ColinEberhardt/7afb58d94428c28c960d243b5299f4d4 to your computer and use it in GitHub Desktop.
Save ColinEberhardt/7afb58d94428c28c960d243b5299f4d4 to your computer and use it in GitHub Desktop.
Simple Crosshair Example
license: mit

This example demonstrates an interactive cross-hair.

  • The crosshair itself is rendered via the crosshair annotation. Notice that the annotation itself is just a 'renderer', i.e. it does not handle mouse / touch events.
  • The pointer component is used to add appropriate event handlers to the plot-area. The pointer component takes the mouse events and converts them into an array, which is the format required by the crosshair.
  • The crosshair location is rendered to a div element that is added to the plot-area by decorating the cartesian chart. By adding the element via the enter selection, we ensure that it is added just once.
<!DOCTYPE html>
<!-- include polyfills for custom event, Symbol and Custom Elements -->
<script src="//unpkg.com/babel-polyfill@6.26.0/dist/polyfill.js"></script>
<script src="//unpkg.com/custom-event-polyfill@0.3.0/custom-event-polyfill.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/document-register-element/1.8.0/document-register-element.js"></script>
<!-- use babel so that we can use arrow functions and other goodness in this block! -->
<script src="//unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="//unpkg.com/d3@5.5.0"></script>
<script src="//unpkg.com/d3fc@14.0.41"></script>
<style>
body {
font: 16px sans-serif;
}
.chart {
height: 480px;
}
.plot-area {
overflow: visible !important;
}
#container {
position: relative;
}
.tooltip {
position: absolute;
top: 10px;
left: 10px;
}
</style>
<div id='simple-chart' class='chart'></div>
<script type='text/babel'>
// create some test data
var sinewave = d3.range(50).map((d) => ({
x: d / 4,
y: Math.sin(d / 4)
}))
// combine the sine wave with an empty array which will hold the
// current crosshair value
var data = {
series: sinewave,
crosshair: []
};
// use the extent component to compute the x / y domain
var yExtent = fc.extentLinear()
.accessors([d => d.y])
.pad([0.4, 0.4])
.padUnit('domain');
var xExtent = fc.extentLinear()
.accessors([d => d.x]);
// create a chart
var chart = fc.chartCartesian(
d3.scaleLinear(),
d3.scaleLinear())
.yDomain(yExtent(sinewave))
.yLabel('Sine')
.xDomain(xExtent(sinewave))
.xLabel('Value')
.decorate(function(sel) {
// decorate the cartesian chart to add the tooltip element on
// the enter selection
sel.enter()
.append('div')
.classed('tooltip', true);
// on the update selection, update the text
sel.select('.tooltip')
.text(d =>
d.crosshair.length > 0
? 'x: ' + d.crosshair[0].x.toFixed(0) + ', y: ' + d.crosshair[0].y.toFixed(0)
: ''
);
})
var sinLine = fc.seriesSvgLine()
.crossValue(d => d.x)
.mainValue(d => d.y);
var crosshair = fc.annotationSvgCrosshair()
.xLabel('')
.yLabel('');
// Add the line and crosshair via a multi-series. Use the mapping function
// to provide the correct data to each
var multi = fc.seriesSvgMulti()
.series([sinLine, crosshair])
.mapping((data, index, series) => {
switch(series[index]) {
case sinLine:
return data.series;
case crosshair:
return data.crosshair;
}
});
chart.svgPlotArea(multi);
function render(){
// render
d3.select('#simple-chart')
.datum(data)
.call(chart);
// add the pointer component to the plot-area, re-rendering
// each time the event fires.
var pointer = fc.pointer()
.on('point', (event) => {
data.crosshair = event;
render();
});
d3.select('#simple-chart .plot-area')
.call(pointer);
}
render();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment