Skip to content

Instantly share code, notes, and snippets.

@jwilber
Last active May 4, 2019 03:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jwilber/6dabb8224d301c4246ee26f50960c9bf to your computer and use it in GitHub Desktop.
Save jwilber/6dabb8224d301c4246ee26f50960c9bf to your computer and use it in GitHub Desktop.
es6 class
license: mit
class Chart {
constructor(opts) {
// load in arguments from config object
this.data = opts.data;
this.element = opts.element;
// create the chart
this.draw();
}
draw() {
// define width, height and margin
this.width = this.element.offsetWidth;
this.height = this.width / 2;
this.margin = {
top: 20,
right: 75,
bottom: 45,
left: 50
};
// set up parent element and SVG
this.element.innerHTML = '';
const svg = d3.select(this.element).append('svg');
svg.attr('width', this.width);
svg.attr('height', this.height);
// we'll actually be appending to a <g> element
this.plot = svg.append('g')
.attr('transform',`translate(${this.margin.left},${this.margin.top})`);
// create the other stuff
this.createScales();
this.addAxes();
this.addLine();
}
createScales() {
// shorthand to save typing later
const m = this.margin;
// calculate max and min for data
const xExtent = d3.extent(this.data, d => d[0]);
const yExtent = d3.extent(this.data, d => d[1]);
// force zero baseline if all data points are positive
if (yExtent[0] > 0) { yExtent[0] = 0; };
this.xScale = d3.scaleTime()
.range([0, this.width-m.right])
.domain(xExtent);
this.yScale = d3.scaleLinear()
.range([this.height-(m.top+m.bottom), 0])
.domain(yExtent);
}
addAxes() {
const m = this.margin;
// create and append axis elements
// this is all pretty straightforward D3 stuff
const xAxis = d3.axisBottom()
.scale(this.xScale)
.ticks(d3.timeMonth);
const yAxis = d3.axisLeft()
.scale(this.yScale)
.tickFormat(d3.format("d"));
this.plot.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${this.height-(m.top+m.bottom)})`)
.call(xAxis);
this.plot.append("g")
.attr("class", "y axis")
.call(yAxis)
}
addLine() {
const line = d3.line()
.x(d => this.xScale(d[0]))
.y(d => this.yScale(d[1]));
this.plot.append('path')
// use data stored in `this`
.datum(this.data)
.classed('line',true)
.attr('d',line)
// set stroke to specified color, or default to red
.style('stroke', this.lineColor || 'red');
}
// the following are "public methods"
// which can be used by code outside of this file
setColor(newColor) {
this.plot.select('.line')
.style('stroke', newColor);
// store for use when redrawing
this.lineColor = newColor;
}
setData(newData) {
this.data = newData;
// full redraw needed
this.draw();
}
}
<!-- load in D3 and Chart constructor scripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<script src="chart.js"></script>
<style>
/* a little bit of CSS to make the chart readable */
.line {
fill: none;
stroke-width: 4px;
}
.axis path, .tick line {
fill: none;
stroke: #333;
}
</style>
<!-- here's the div our chart will be injected into -->
<div class="chart-container" style="max-width: 1000px;"></div>
<script>
// create new chart using Chart constructor
const chart = new Chart({
element: document.querySelector('.chart-container'),
data: [
[new Date(2016,0,1), 10],
[new Date(2016,1,1), 70],
[new Date(2016,2,1), 30],
[new Date(2016,3,1), 10],
[new Date(2016,4,1), 40]
]
});
// redraw chart on each resize
// in a real-world example, it might be worth ‘throttling’ this
// more info: http://sampsonblog.com/749/simple-throttle-function
d3.select(window).on('resize', () => chart.draw() );
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment