D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
bthow
Full window
Github gist
tinkerChart
Built with
blockbuilder.org
<!DOCTYPE html> <div> <button value=1>1day</button> <button value=3>3days</button> <button value=7>7days</button> <button value=30>30days</button> </div> <script src="https://d3js.org/d3.v4.min.js"></script> <link rel="stylesheet" href="cStyle.css"> <script> /************************/ // config from sencha var cfg = [ { title: 'Temperature', unit: 'oC', cls: 'tmp', height: 80, displayRange: [34,42], type: 'line', columnName: 'TEMPERATURE' }, { title: 'Pulse', unit: 'BPM', cls: 'hr', height: 90, displayRange: [40,180], type: 'line', columnName: 'HEART_RATE' }, { title: 'Lying Blood Pressure', cls: 'bp', unit: '', height: 180, displayRange: [20,260], type: 'BP', columnName: 'LYING_SBP', columnName2: 'LYING_DBP' } ]; var d3c = d3c || { parseTime: d3.utcParse("%Y-%m-%d %H:%M:%S.%L"), formatTime: d3.utcFormat("%e %B"), plot: [], x:{}, // shared x axis function xAxis:{}, // timeExtent: [], //plotG: [], // // y:[], // Array of y axises // tip: [] }; // Setup size & margins var margin = {top: 30, right: 20, bottom: 20, left: 30, gapBetweenCharts: 40}, width = 1000 - margin.left - margin.right, height = margin.top + margin.bottom; for(var i=0; i<cfg.length; i++) { height += cfg[i].height + margin.gapBetweenCharts; } height = height - margin.gapBetweenCharts; d3c.setup = function(cfg, height, width, margin){ d3c.x = d3.scaleTime().rangeRound([0, width]); /************************/ // Add the canvases for(var i=0; i<cfg.length; i++) { d3c.plot.push({ g: null, //plotG line: null, // line series area: null, // line shading circle: null, // hover marker mline: null, // not working y: null, // plot y axis yAxis:{},// render y axis tip:null, data: [] // datetime/value data required for the plot }); var s = d3.select("body").append("svg") .attr("class", cfg[i].cls) .attr("width", width + margin.left + margin.right) .attr("height", cfg[i].height + margin.top + margin.bottom); d3c.plot[i].g = s.append("g") .attr("class", "g-cls") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); s.append("defs").append("clipPath") .attr("id", "clip") .append("rect") .attr("width", width) .attr("height", cfg[i].height); d3c.plot[i].y= d3.scaleLinear().rangeRound([cfg[i].height, 0]); d3c.plot[i].tip = d3.select("body").append("div") .attr("class", "tooltip tip_" + i) .style("opacity", 0); } for(var i=0; i<cfg.length; i++) { d3c.plot[i].line = d3.line() .defined(function (d) { return d.value !== 0; }) // exclude 0 (NULL) .x(function(d) { return d3c.x(d.date); }) .y(function(d) { return d3c.plot[d.idx].y(d.value); }); d3c.plot[i].area = d3.area() .defined(d3c.plot[i].line.defined()) .x(d3c.plot[i].line.x()) .y1(d3c.plot[i].line.y()) .y0(d3c.plot[i].y(0)); } // Load the data d3.tsv("data.tsv", function(d) { d.date = d3c.parseTime(d.OBS_TAKEN_DTTM); for(var i=0; i<cfg.length; i++) { // convert column values to int and replace null as 0 d[cfg[i].columnName] = +(d[cfg[i].columnName]=='NULL'?0:d[cfg[i].columnName]); d3c.plot[i].data.push({ idx: i, date:d.date, value:d[cfg[i].columnName], value2:d[cfg[i].columnName2] }); } return d; }, function(error, data) { if (error) throw error; // Get max/min Extent of date data d3c.timeExtent = d3.extent(data, function(d) { return d.date; }); // x-axis min/max Set min extent 3 days before last data item var startExtent = new Date(d3c.timeExtent[1].getTime() + -5*24*3600000 /*days*/); var jj = d3c.getTimeExtent(3); console.log(jj); d3c.x.domain(d3c.getTimeExtent(3));// d3c.xAxis = d3.axisBottom(d3c.x).ticks(6); // y-axis min/max static range from config for(var i=0; i<cfg.length; i++) { d3c.plot[i].y.domain(cfg[i].displayRange); d3c.plot[i].yAxis = d3.axisLeft(d3c.plot[i].y) .ticks(5) .tickSize(-width); } /************************/ // Draw each chart for(var i=0; i<cfg.length; i++) { // Draw x-axis time var g = d3c.plot[i].g; g.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + cfg[i].height + ")") .call(d3c.xAxis); var pan = g.append("g").attr("class", "y pan"); // Draw y-axis g.append("g").attr("class", "axis axis--y") .call(d3c.plot[i].yAxis) .append("text") //axis label .attr("class", "title " + cfg[i].cls) .attr("y", -7) .text(cfg[i].title); // Draw Type = Line if(cfg[i].type=='line'){ pan.append("path") .attr("class", "ll line " + cfg[i].cls) .datum(d3c.plot[i].data) .attr("fill", "none") .attr("stroke", "steelblue") .attr("stroke-linejoin", "round") .attr("stroke-linecap", "round") .attr("stroke-width", 1.5) .attr("d", d3c.plot[i].line); pan.append("path").attr("class", "ll area " + cfg[i].cls) .datum(d3c.plot[i].data) .attr("d", d3c.plot[i].area[i]) } // Draw Type = BP if(cfg[i].type=='BP'){ var dataBar = d3c.plot[i].data.filter(function(d){ return d.value !== 0 && d.value2 !== 0; }); var b = pan.selectAll("BP") .data(dataBar) .enter().append("g") .attr("class", "BP bar " + cfg[i].cls) .attr("transform", function(d) { return "translate(" + d3c.x(d.date)+ ",0)"}); b.append("rect") //middle .attr("width", 1) .attr("y", function(d) { return d3c.plot[i].y(d.value)}) .attr("height", function(d) { return d3c.plot[i].y(d.value2) - d3c.plot[i].y(d.value); }) b.append("rect") //head cap .attr("x", -3) .attr('y', function(d) { return d3c.plot[i].y(d.value)}) .attr("width", 7.3) .attr("height", 1); b.append("rect") // foot cap .attr("x", -3 ) .attr('y', function(d) { return d3c.plot[i].y(d.value2)}) .attr("width", 7) .attr("height", 1); } /************************/ // Draw marker circle and line d3c.plot[i].circle = pan.append("circle") .attr("r",3) .style("opacity", 0) .attr("class", cfg[i].cls); d3c.plot[i].mline = pan.append("line") .attr("x1",0).attr("x2",0) .attr("y1",0).attr("y2",cfg[i].height) .attr("class", "mline"); var et = [ [ d3c.x(d3c.timeExtent[0]), 0 ], [ d3c.x(d3c.timeExtent[1]), cfg[i].height ] ]; // x-pan only var zoom = d3.zoom() .scaleExtent([1,1]) //Dont scale .translateExtent(et) //Dont pan-y .on("zoom", d3c.zoomed); d3.selectAll("svg") .call(zoom) // hover panel for tooltip g.append("rect").attr("class", "hoverPanel " + cfg[i].cls) .attr("width", width) .attr("height", cfg[i].height) .style('fill', 'transparent') .on('mousemove', d3c.identifyMouse); //tip.push(g.append("div") // .attr("class", "tooltip tip_" + i) //.style("opacity", 0)); } }); } d3c.zoomed = function() { var t = d3.event.transform; t.y = 0; xt = t.rescaleX(d3c.x); // if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush d3.selectAll("svg").selectAll("bar").attr("transform", t); //x.domain(getTimeExtent(days)); for(var i=0; i<cfg.length; i++) { var plot = d3c.plot[i]; plot.g.select(".line").attr("d", plot.line.x(function(d) { return xt(d.date); })); plot.g.select(".area").attr("d", plot.area.x(function(d) { return xt(d.date); })); plot.g.select(".axis--x").call(d3c.xAxis.scale(xt)); } } d3c.getTimeExtent = function (days){ var start = new Date(d3c.timeExtent[1].getTime() + -days*24*3600000 /*days*/); return [start, d3c.timeExtent[1]]; } // scale x-axis d3.selectAll("button").on("click", function(){ var days = d3.select(this).attr('value'); d3c.x.domain(d3c.getTimeExtent(days)); for(var i=0; i<cfg.length; i++) { var plot = d3c.plot[i]; plot.g.select(".axis--x").transition() .duration(2500) .call(d3c.xAxis); plot.g.select(".line").transition() .duration(2500) .attr("d", plot.line); plot.g.select(".area").transition() .duration(2500) .attr("d", plot.area); plot.g.selectAll(".bar").attr("transform", d3.zoom().scale ); } }); d3c.identifyMouse =function(){ var bisect = d3.bisector(function(d){ return d.date; }).left; var coordinates = d3.mouse(this); var xInv = d3c.x.invert( coordinates[0] ); var yOffset = -10; for(var i=0; i<cfg.length; i++) { var plot = d3c.plot[i]; var dataBar = plot.data.filter(function(d){ return d.value !== 0 && d.value2 !== 0; }); var pos = bisect(dataBar, xInv, 0, dataBar.length); var smaller = dataBar[pos-1]; var larger = dataBar[pos]; var match = xInv - smaller.x < larger.x - xInv ? smaller : larger; plot.tip.transition() .duration(200) .style("opacity", .9) .transition().delay(1000) .duration(400).style("opacity", 0); plot.tip.html(d3c.formatTime(match.date) + "<br/>" + match.value) .style("left", d3c.x(match.date) + "px") .style("top", yOffset + plot.y(match.value) + "px"); yOffset += cfg[i].height + margin.gapBetweenCharts; plot.circle.transition() .duration(70) .style("opacity", .9) .attr('cx', d3c.x(match.date)) .attr('cy', plot.y(match.value)) .transition().delay(1000) .duration(400).style("opacity", 0); plot.mline .attr("x1", d3c.x(match.date)) .attr("x2", d3c.x(match.date)) } } d3c.setup(cfg, height, width, margin); </script>
https://d3js.org/d3.v4.min.js