Skip to content

Instantly share code, notes, and snippets.

@hironow
Last active November 9, 2015 16:32
Show Gist options
  • Save hironow/6e3a45b47d256af55e6d to your computer and use it in GitHub Desktop.
Save hironow/6e3a45b47d256af55e6d to your computer and use it in GitHub Desktop.
D3 linepoints chart by datasets with data tooltip
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Title</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<div id="container"></div>
<script src="main.js"></script>
</body>
</html>
#container {
background-color: #EEE;
}
.data-tooltip {
position: absolute;
color: #fff;
background-color: #333;
}
.data-tooltip-arrow {
width: 0;
height: 0;
border-right: 5px solid transparent;
border-left: 5px solid transparent;
border-top: 10px solid #000;
position: absolute;
bottom: -10px;
}
.axis path,
.axis line {
fill: none;
stroke: #333;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
path.line {
fill: none;
stroke: #333;
stroke-width: 2px;
}
text.label {
fill: #333;
stroke: none;
}
var newDatasets = [
{
meta: {
id: 1,
name: 'A'
},
data: [
{x: 10, y: 10},
{x: 20, y: 20},
{x: 30, y: 30},
{x: 40, y: 40},
{x: 50, y: 50}
]
},
{
meta: {
id: 3,
name: 'C'
},
data: [
{x: 100, y: 10},
{x: 200, y: 20},
{x: 300, y: 30},
{x: 400, y: 40},
{x: 500, y: 50}
]
}
];
var firstDatasets = [
{
meta: {
id: 1,
name: 'A'
},
data: [
{x: 10, y: 10},
{x: 20, y: 20},
{x: 30, y: 30},
{x: 40, y: 40},
{x: 50, y: 50}
]
},
{
meta: {
id: 2,
name: 'B'
},
data: [
{x: 10, y: 1},
{x: 20, y: 2},
{x: 30, y: 3},
{x: 40, y: 4},
{x: 50, y: 5}
]
}
];
var __width = 500;
var __height = 500;
var _margin = {top: 50, right: 50, bottom: 50, left: 50};
var getId = function(dataset) { return dataset.meta.id; };
var getLabel = function(dataset) { return dataset.meta.name; };
var getData = function(dataset) { return dataset.data; };
var getLastData = function(dataset) { return dataset.data[dataset.data.length - 1]; };
var getX = function(data) { return data.x; };
var getY = function(data) { return data.y; };
var _width = __width - _margin.left - _margin.right;
var _height = __height - _margin.top - _margin.bottom;
var xScale = d3.scale.linear().range([0, _width]);
var yScale = d3.scale.linear().range([_height, 0]);
var line = d3.svg.line().interpolate('linear')
.x(function(data) { return xScale(getX(data)); })
.y(function(data) { return yScale(getY(data)); })
var dataTooltip = d3.select('#container')
.append('div')
.classed('data-tooltip', true);
var svg = d3.select('#container')
.append('svg')
.attr('width', __width)
.attr('height', __height);
var g = svg.append('g')
.attr('transform',
'translate(' + _margin.left + ',' + _margin.top + ')');
render(firstDatasets);
function render(datasets) {
var xs = _.chain(datasets).map(getData)
.flatten().map(getX).value();
var ys = _.chain(datasets).map(getData)
.flatten().map(getY).value();
xScale.domain(d3.extent(xs));
yScale.domain(d3.extent(ys));
var xAxis = d3.svg.axis().scale(xScale).orient('bottom');
var yAxis = d3.svg.axis().scale(yScale).orient('left');
if (g.selectAll('g.x.axis').empty()) {
g.append('g')
.classed('x axis', true)
.attr('transform', 'translate(0,' + _height + ')')
.call(xAxis);
} else {
g.selectAll('g.x.axis')
.transition().duration(1000)
.call(xAxis);
}
if (g.selectAll('g.y.axis').empty()) {
g.append('g')
.classed('y axis', true)
.call(yAxis);
} else {
g.selectAll('g.y.axis')
.transition().duration(1000)
.call(yAxis);
}
// bind dataset
var datasetContainers = g.selectAll('g.dataset')
.data(datasets, getId);
// enter
datasetContainers
.enter().append('g')
.classed('dataset', true)
.attr('id', function(dataset) { return getId(dataset); })
.on('mouseover', function(dataset) {
var self = this;
var gs = d3.selectAll('g.dataset');
gs.filter(function() { return self.id !== this.id; })
.transition().duration(1000)
.style('opacity', 0.2);
})
.on('mouseout', function(dataset) {
var self = this;
var gs = d3.selectAll('g.dataset');
gs.filter(function() { return self.id !== this.is; })
.transition().duration(1000)
.style('opacity', 1.0);
})
.style('opacity', 0.0)
.transition().duration(1000)
.style('opacity', 1.0);
datasetContainers.selectAll('path.line')
.data(function(dataset) { return [dataset]; })
.enter().append('path')
.classed('line', true)
.attr('d', function(dataset) { return line(getData(dataset)); });
datasetContainers.selectAll('circle.point')
.data(function(dataset) { return getData(dataset); })
.enter().append('circle')
.classed('point', true)
.attr('cx', function(data) { return xScale(getX(data)); })
.attr('cy', function(data) { return yScale(getY(data)); })
.attr('r', 4)
.on('mouseover', function(data) {
d3.select(this)
.transition().duration(1000)
.attr('r', 5)
.style('stroke-width', '3px');
var dataTooltipContainer = dataTooltip.data([data]);
dataTooltipContainer.append('div')
.classed('x', true)
.html(getX);
dataTooltipContainer.append('div')
.classed('y', true)
.html(getY);
dataTooltipContainer.append('div')
.classed('data-tooltip-arrow', true);
dataTooltip
.transition().duration(1000)
.style('left', (+d3.select(this).attr('cx') + _margin.left) + 'px')
.style('top', (+d3.select(this).attr('cy') + _margin.top) + 'px')
.style('opacity', 1)
.style('display', 'block');
})
.on('mouseout', function(data) {
d3.select(this)
.transition().duration(1000)
.attr('r', 4)
.style('stroke-width', '2px');
// hide tooltip
dataTooltip
.transition().duration(1000)
.style('opacity', 0)
.style('display', 'none');
dataTooltip.selectAll('.x').remove();
dataTooltip.selectAll('.y').remove();
dataTooltip.selectAll('.data-tooltip-arrow').remove();
});
datasetContainers.selectAll('text.label')
.data(function(dataset) { return [dataset]; })
.enter().append('text')
.classed('label', true)
.attr('x', function(dataset) { return xScale(getX(getLastData(dataset))); })
.attr('y', function(dataset) { return yScale(getY(getLastData(dataset))); })
.text(getLabel);
// exit
datasetContainers
.exit()
.transition().duration(1000)
.style('opacity', 0.0)
.remove();
// update
datasetContainers
.selectAll('path.line')
.transition().duration(1000)
.attr('d', function(dataset) { return line(getData(dataset)); });
datasetContainers
.selectAll('circle.point')
.transition().duration(1000)
.attr('cx', function(data) { return xScale(getX(data)); })
.attr('cy', function(data) { return yScale(getY(data)); });
datasetContainers
.selectAll('text.label')
.transition().duration(1000)
.attr('x', function(dataset) { return xScale(getX(getLastData(dataset))); })
.attr('y', function(dataset) { return yScale(getY(getLastData(dataset))); });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment