I am trying to integrate this D3 area graph into Vuejs and all I get from my code is the axes with a black area. It seems that the CSS properties have no effect on my graph and everything I have tried so far didn't change anything.. Most of the D3 graphs I have tried with Vuejs have similar problems.. Any ideas?
<template>
<div class="line-chart"></div>
</template>
<script>
import * as d3 from 'd3'
export default {
name: 'line-chart',
mounted () {
this.renderChart()
},
data() {
return {
data: [
{ date: '1-May-12', close: 58.13 },
{ date: '30-Apr-12', close: 53.98 },
{ date: '27-Apr-12', close: 67.00 },
{ date: '26-Apr-12', close: 89.70 },
{ date: '25-Apr-12', close: 99.00 },
{ date: '24-Apr-12', close: 130.28 },
{ date: '23-Apr-12', close: 166.70 },
{ date: '20-Apr-12', close: 234.98 },
{ date: '19-Apr-12', close: 345.44 },
{ date: '18-Apr-12', close: 443.34 },
{ date: '17-Apr-12', close: 543.70 },
{ date: '16-Apr-12', close: 580.13 },
{ date: '13-Apr-12', close: 605.23 },
{ date: '12-Apr-12', close: 622.77 },
{ date: '11-Apr-12', close: 626.20 },
{ date: '10-Apr-12', close: 628.44 },
{ date: '9-Apr-12', close: 636.23 },
{ date: '5-Apr-12', close: 633.68 },
{ date: '4-Apr-12', close: 624.31 },
{ date: '3-Apr-12', close: 629.32 },
{ date: '2-Apr-12', close: 618.63 },
{ date: '30-Mar-12', close: 599.55 },
{ date: '29-Mar-12', close: 609.86 },
{ date: '28-Mar-12', close: 617.62 },
{ date: '27-Mar-12', close: 614.48 },
{ date: '26-Mar-12', close: 606.98 }
],
width: 960,
height: 500
}
},
methods: {
renderChart() {
var data = this.data;
var margin = { top: 20, right: 20, bottom: 30, left: 50 }
var width = this.width - margin.left - margin.right;
var height = this.height - margin.top - margin.bottom;
var parseTime = d3.timeParse('%d-%b-%y');
// parse the date / time
var parseTime = d3.timeParse("%d-%b-%y");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the area
var area = d3.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.close); });
// define the line
var valueline = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr(
"transform",
"translate(" + margin.left + "," + margin.top + ")");
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
// scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// add the area
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", area);
// add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
},
}
};
</script>
<style scoped>
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.area {
fill: lightsteelblue;
}
</style>
I would change your div from a class of line-chart to an id of line-chart, then, where you have d3.select('body'), change it to d3.select('#line-chart'). That should append the graph to the write place.
Also, make sure you've installed d3v4 and not d3v3. There are serious breaking changes.
Edited to add: here's a working example in the Stack Overflow snippet runner thing. It's not a single-file component anymore but it's essentially identical.
new Vue({
el: '#line-chart',
mounted () {
this.renderChart()
setTimeout(()=>{
this.data = [
{ date: '1-May-12', close: 58.13 },
{ date: '30-Apr-12', close: 53.98 },
{ date: '27-Apr-12', close: 67.00 },
{ date: '26-Apr-12', close: 89.70 },
{ date: '25-Apr-12', close: 99.00 },
{ date: '24-Apr-12', close: 130.28 },
{ date: '23-Apr-12', close: 166.70 },
{ date: '20-Apr-12', close: 234.98 },
{ date: '19-Apr-12', close: 345.44 },
{ date: '18-Apr-12', close: 443.34 },
{ date: '17-Apr-12', close: 543.70 },
{ date: '16-Apr-12', close: 580.13 },
{ date: '13-Apr-12', close: 605.23 },
{ date: '12-Apr-12', close: 622.77 },
{ date: '11-Apr-12', close: 626.20 },
{ date: '10-Apr-12', close: 628.44 },
{ date: '9-Apr-12', close: 636.23 },
{ date: '5-Apr-12', close: 633.68 },
{ date: '4-Apr-12', close: 624.31 },
{ date: '3-Apr-12', close: 629.32 },
{ date: '2-Apr-12', close: 618.63 },
{ date: '30-Mar-12', close: 599.55 },
{ date: '29-Mar-12', close: 609.86 },
{ date: '28-Mar-12', close: 617.62 },
{ date: '27-Mar-12', close: 614.48 },
{ date: '26-Mar-12', close: 606.98 }
]}, 1000)
},
data() {
return {
data: [],
width: 960,
height: 500
}
},
methods: {
renderChart() {
if (document.getElementsByTagName('svg')){
d3.selectAll('svg').remove()
}
var data = this.data;
var margin = { top: 20, right: 20, bottom: 30, left: 50 }
var width = this.width - margin.left - margin.right;
var height = this.height - margin.top - margin.bottom;
var parseTime = d3.timeParse('%d-%b-%y');
// parse the date / time
var parseTime = d3.timeParse("%d-%b-%y");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the area
var area = d3.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.close); });
// define the line
var valueline = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
var svg = d3.select("#line-chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
// scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// add the area
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", area);
// add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
},
},
watch: {
data(val) {
if (val){
this.renderChart()
}
}
}
})
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.area {
fill: lightsteelblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.5.0/d3.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="line-chart"></div>
Related
Based on Simple graph with grid lines in v4 (for example), I would like to add laced bands between grid lines, similar to the picture below:
How could I achieve that?
Thanks.
I was able to solve this using rect elements and enabling opacity based on their index. First I get all the values of the yaxis into an array so that I can get the y attribute for each rect and then just append those rects in a group g.
// add the Y Axis
svg.append("g").attr('class', 'y axis')
.call(d3.axisLeft(y));
//Store all values of the y-axis to an array
var yval = []
d3.selectAll('.y.axis>g>text').each(function(d) {
yval.push(d);
});
// Create rects and assign opacity based on index
rects.selectAll('rect').data(yval).enter().append('rect')
.attr('x', 0).attr('y', function(d) { return y(d); })
.attr('height', height / yval.length)
.attr('width', width).style('fill-opacity', function(d, i) {
if (i == 0) {
return 0;
}
if (i % 2 == 0) {
return 0.1;
} else {
return 0;
}
});
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parse the date / time
var parseTime = d3.timeParse("%d-%b-%y");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the line
var valueline = d3.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close);
});
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// gridlines in x axis function
function make_x_gridlines() {
return d3.axisBottom(x)
.ticks(5)
}
// gridlines in y axis function
function make_y_gridlines() {
return d3.axisLeft(y)
.ticks(5)
}
// Get the data
var data = [{
"date": "1-May-12",
"close": 58.13
},
{
"date": "30-Apr-12",
"close": 53.98
},
{
"date": "27-Apr-12",
"close": 67
},
{
"date": "26-Apr-12",
"close": 89.7
},
{
"date": "25-Apr-12",
"close": 99
},
{
"date": "24-Apr-12",
"close": 130.28
},
{
"date": "23-Apr-12",
"close": 166.7
},
{
"date": "20-Apr-12",
"close": 234.98
},
{
"date": "19-Apr-12",
"close": 345.44
},
{
"date": "18-Apr-12",
"close": 443.34
},
{
"date": "17-Apr-12",
"close": 543.7
},
{
"date": "16-Apr-12",
"close": 580.13
},
{
"date": "13-Apr-12",
"close": 605.23
},
{
"date": "12-Apr-12",
"close": 622.77
},
{
"date": "11-Apr-12",
"close": 626.2
},
{
"date": "10-Apr-12",
"close": 628.44
},
{
"date": "9-Apr-12",
"close": 636.23
},
{
"date": "5-Apr-12",
"close": 633.68
},
{
"date": "4-Apr-12",
"close": 624.31
},
{
"date": "3-Apr-12",
"close": 629.32
},
{
"date": "2-Apr-12",
"close": 618.63
},
{
"date": "30-Mar-12",
"close": 599.55
},
{
"date": "29-Mar-12",
"close": 609.86
},
{
"date": "28-Mar-12",
"close": 617.62
},
{
"date": "27-Mar-12",
"close": 614.48
},
{
"date": "26-Mar-12",
"close": 606.98
}
]
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.close;
})]);
// add the X gridlines
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_gridlines()
.tickSize(-height)
.tickFormat("")
)
// add the Y gridlines
svg.append("g")
.attr("class", "grid")
.call(make_y_gridlines()
.tickSize(-width)
.tickFormat("")
)
var rects = svg.append('g').attr('class', 'intBands')
// add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the Y Axis
svg.append("g").attr('class', 'y axis')
.call(d3.axisLeft(y));
var yval = []
d3.selectAll('.y.axis>g>text').each(function(d) {
yval.push(d);
});
rects.selectAll('rect').data(yval).enter().append('rect')
.attr('x', 0).attr('y', function(d) {
return y(d)
}).attr('height', height / yval.length).attr('width', width).style('fill-opacity', function(d, i) {
if (i == 0) {
return 0;
}
if (i % 2 == 0) {
return 0.1;
} else {
return 0;
}
});
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.grid line {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
/* set the CSS */
</style>
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
</script>
</body>
If anyone has a better way of doing this, please post your solution. Thanks.
One way consists in getting ranges between grid lines from the y-axis and for each of them to include a rectangle:
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parse the date / time
var parseTime = d3.timeParse("%d-%b-%y");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the line
var valueline = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// gridlines in x axis function
function make_x_gridlines() {
return d3.axisBottom(x)
.ticks(5)
}
// gridlines in y axis function
function make_y_gridlines() {
return d3.axisLeft(y)
.ticks(5)
}
// Get the data
var data = [
{ date: "1-May-12", close: 58.13 },
{ date: "30-Apr-12", close: 53.98 },
{ date: "27-Apr-12", close: 67.00 },
{ date: "26-Apr-12", close: 89.70 },
{ date: "25-Apr-12", close: 99.00 },
{ date: "24-Apr-12", close: 130.28 },
{ date: "23-Apr-12", close: 166.70 },
{ date: "20-Apr-12", close: 234.98 },
{ date: "19-Apr-12", close: 345.44 },
{ date: "18-Apr-12", close: 443.34 },
{ date: "17-Apr-12", close: 543.70 },
{ date: "16-Apr-12", close: 580.13 },
{ date: "13-Apr-12", close: 605.23 },
{ date: "12-Apr-12", close: 622.77 },
{ date: "11-Apr-12", close: 626.20 },
{ date: "10-Apr-12", close: 628.44 },
{ date: "9-Apr-12", close: 636.23 },
{ date: "5-Apr-12", close: 633.68 },
{ date: "4-Apr-12", close: 624.31 },
{ date: "3-Apr-12", close: 629.32 },
{ date: "2-Apr-12", close: 618.63 },
{ date: "30-Mar-12", close: 599.55 },
{ date: "29-Mar-12", close: 609.86 },
{ date: "28-Mar-12", close: 617.62 },
{ date: "27-Mar-12", close: 614.48 },
{ date: "26-Mar-12", close: 606.98 }
]
// d3.csv("data.csv").then(data) {
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// add the X gridlines
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_gridlines()
.tickSize(-height)
.tickFormat("")
)
// add the Y gridlines
svg.append("g")
.attr("class", "grid ylines")
.call(make_y_gridlines()
.tickSize(-width)
.tickFormat("")
)
// add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
var bands = [];
var start;
d3.selectAll(".ylines .tick line")
.each(function(d, i) {
if (i % 2 == 0)
start = d;
else {
bands.push({ "start": start, "end": d })
start = null;
}
});
// If it remains the top band:
if (start)
bands.push({ "start": start, "end": d3.max(data, function(d) { return d.close; }) })
svg.append("g").selectAll("band")
.data(bands)
.enter().append("rect")
.attr("y", d => y(d.end))
.attr("height", d => y(d.start) - y(d.end))
.attr("x", d => 0)
.attr("width", d => width)
.style("opacity", 0.1)
.style("stroke", "#005e23")
.style("fill", "#005e23");
// });
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.grid line {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
<body>
<script src="https://d3js.org/d3.v5.min.js"></script>
</body>
// Let's retrieve from the grid lines the associated bands y-extremities:
var bands = [];
var start;
d3.selectAll(".ylines .tick line")
.each(function(d, i) {
if (i % 2 == 0)
start = d;
else {
bands.push({ "start": start, "end": d })
start = null; // in order to know if we should use the top band
}
});
// If it remains a top band:
if (start)
bands.push({ "start": start, "end": d3.max(data, function(d) { return d.close; }) })
// Our bands look like:
// [{start: 0,end: 100}, {start: 200,end: 300}, ..., {start: 600,end: 636.23}]
svg.append("g").selectAll("band")
.data(bands)
.enter().append("rect")
.attr("y", d => y(d.end))
.attr("height", d => y(d.start) - y(d.end))
.attr("x", d => 0)
.attr("width", d => width)
.style("opacity", 0.1)
.style("stroke", "#005e23")
.style("fill", "#005e23");
Using grid lines rather than ticks gives the choice to have more ticks than grid lines.
I am trying to follow this https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172
Here is my fiddle: https://jsfiddle.net/q0xece4k/17/'
<!DOCTYPE html>
<meta charset="utf-8">
<title>Plotting a Trendline with D3.js</title>
<style>
.line {
stroke: blue;
fill:none;
stroke-width: 3;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-size: 10px;
font-family: sans-serif;
}
.text-label {
font-size: 10px;
font-family: sans-serif;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
<div>
<div id="container"></div>
</div>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script>
var height = 300;
var width = 600;
var margin = {top: 10, right:20, bottom: 50, left: 40};
// formatters for axis and labels
var rateFormat = d3.format("0.1f");
var trendDataArr = [
{
date: "2017-01-29",
value: 87.1
},
{
date: "2017-02-06",
value: 88
},
{
date: "2017-02-13",
value: 86.8
},
{
date: "2017-02-20",
value: 86.8
},
{
date: "2017-02-27",
value: 87.1
},
{
date: "2017-03-05",
value: 85.8
},
{
date: "2017-03-12",
value: 85.5
},
{
date: "2017-03-19",
value: 87.1
},
{
date: "2017-03-26",
value: 88
},
{
date: "2017-04-02",
value: 87.4
},
{
date: "2017-04-09",
value: 86.8
},
{
date: "2017-04-16",
value: 87.1
},
{
date: "2017-04-23",
value: 87.4
},
{
date: "2017-04-30",
value: 85.8
},
{
date: "2017-05-07",
value: 86.5
},
{
date: "2017-05-14",
value: 87.1
},
{
date: "2017-05-21",
value: 86.9
},
{
date: "2017-05-28",
value: 87.7
},
{
date: "2017-06-04",
value: 87.8
}
];
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var svg = d3.select('#container')
.append("svg")
.attr("pointer-events", "all")
.attr("width", 1000 + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.append("g");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
svg.append("g")
.attr("class", "y axis");
svg.append("g")
.attr("class", "x axis");
function getDate(d) {
return moment(d["date"],"YYYY-MM-DD").toDate();
}
function removeDuplicates(xLabels){
var result = [];
xLabels.forEach(function(item) {
if(result.indexOf(item) < 0) {
result.push(item);
}
return result;
});
}
var minDate = getDate(trendDataArr[0]),
maxDate = getDate(trendDataArr[trendDataArr.length-1]);
var xScale = d3.scaleTime()
// .rangeBands([margin.left, width-100], .1);
.domain([minDate, maxDate])
.range([margin.left, width])
// .nice(trendDataArr.length);
var yScale = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(xScale)
.tickFormat(d3.timeFormat('%d %b %y'));
var yAxis = d3.axisLeft(yScale);
var xLabels = trendDataArr.map(function (d) { return getDate(d); });
xScale.domain(d3.extent(xLabels));
yScale.domain([Math.round(d3.min(trendDataArr, function(d) { return parseFloat(d['value']); }))-5, Math.round(d3.max(trendDataArr, function(d) { return parseFloat(d['value']); }))+5]);
var line = d3.line()
.x(function(d) { return xScale(getDate(d)); })
.y(function(d) { return yScale(d['value']); })
.curve(d3.curveCardinal);
svg.append("path")
.datum(trendDataArr)
.attr('fill','none')
.attr("d", line(trendDataArr.filter(function(d) {
return d;
}
)
)
)
.attr('stroke', "steelblue")
.attr('stroke-width', 2);
svg.select(".x.axis")
.attr("transform", "translate(0," + (height+5) + ")")
.call(xAxis.tickFormat(d3.timeFormat("%d %b %y")))
.selectAll("text")
.style("text-anchor","end")
.attr("transform", function(d) {
return "rotate(-45) ";
});
svg.select(".y.axis")
.attr("transform", "translate(" + (margin.left) + ",0)")
.call(yAxis.tickFormat(rateFormat));
// chart title
svg.append("text")
.attr("x", (width + (margin.left + margin.right) )/ 2 -200)
.attr("y", 0 + margin.top -20)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-family", "sans-serif")
.text("Zoomable chart");
//y axis label
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -20)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Rate");
function zoomed() {
var t = d3.event.transform;
xScale.domain(t.rescaleX(xScale).domain());
}
</script>
</body>
Could someone help me with the zooming in/out on the axis so that I can drill into each day or zoom out to years?
I got it to work:
here is the updated fiddle:
https://jsfiddle.net/q0xece4k/17/
var height = 300;
var width = 600;
var margin = {top: 10, right:20, bottom: 50, left: 40};
// formatters for axis and labels
var rateFormat = d3.format("0.1f");
var trendDataArr = [
{
date: "2017-01-29",
value: 87.1
},
{
date: "2017-02-06",
value: 88
},
{
date: "2017-02-13",
value: 86.8
},
{
date: "2017-02-20",
value: 86.8
},
{
date: "2017-02-27",
value: 87.1
},
{
date: "2017-03-05",
value: 85.8
},
{
date: "2017-03-12",
value: 85.5
},
{
date: "2017-03-19",
value: 87.1
},
{
date: "2017-03-26",
value: 88
},
{
date: "2017-04-02",
value: 87.4
},
{
date: "2017-04-09",
value: 86.8
},
{
date: "2017-04-16",
value: 87.1
},
{
date: "2017-04-23",
value: 87.4
},
{
date: "2017-04-30",
value: 85.8
},
{
date: "2017-05-07",
value: 86.5
},
{
date: "2017-05-14",
value: 87.1
},
{
date: "2017-05-21",
value: 86.9
},
{
date: "2017-05-28",
value: 87.7
},
{
date: "2017-06-04",
value: 87.8
}
];
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var svg = d3.select('#container')
.append("svg")
.attr("pointer-events", "all")
.attr("width", 1000 + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.append("g");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
svg.append("g")
.attr("class", "y axis");
svg.append("g")
.attr("class", "x axis");
function getDate(d) {
return moment(d["date"],"YYYY-MM-DD").toDate();
}
function removeDuplicates(xLabels){
var result = [];
xLabels.forEach(function(item) {
if(result.indexOf(item) < 0) {
result.push(item);
}
return result;
});
}
var minDate = getDate(trendDataArr[0]),
maxDate = getDate(trendDataArr[trendDataArr.length-1]);
var xScale = d3.scaleTime()
// .rangeBands([margin.left, width-100], .1);
.domain([minDate, maxDate])
.range([margin.left, width])
// .nice(trendDataArr.length);
var yScale = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(xScale)
.tickFormat(d3.timeFormat('%d %b %y'));
var yAxis = d3.axisLeft(yScale);
var xLabels = trendDataArr.map(function (d) { return getDate(d); });
xScale.domain(d3.extent(xLabels));
yScale.domain([Math.round(d3.min(trendDataArr, function(d) { return parseFloat(d['value']); }))-5, Math.round(d3.max(trendDataArr, function(d) { return parseFloat(d['value']); }))+5]);
var line = d3.line()
.x(function(d) { return xScale(getDate(d)); })
.y(function(d) { return yScale(d['value']); })
.curve(d3.curveCardinal);
svg.append("path")
.datum(trendDataArr)
.attr('fill','none')
.attr("d", line(trendDataArr.filter(function(d) {
return d;
}
)
)
)
.attr('stroke', "steelblue")
.attr('stroke-width', 2);
svg.select(".x.axis")
.attr("transform", "translate(0," + (height+5) + ")")
.call(xAxis.tickFormat(d3.timeFormat("%d %b %y")))
.selectAll("text")
.style("text-anchor","end")
.attr("transform", function(d) {
return "rotate(-45) ";
});
svg.select(".y.axis")
.attr("transform", "translate(" + (margin.left) + ",0)")
.call(yAxis.tickFormat(rateFormat));
// chart title
svg.append("text")
.attr("x", (width + (margin.left + margin.right) )/ 2 -200)
.attr("y", 0 + margin.top -20)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-family", "sans-serif")
.text("Zoomable chart");
//y axis label
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -20)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Rate");
function zoomed() {
var t = d3.event.transform;
xScale.domain(t.rescaleX(xScale).domain());
}
You need to call xAxis again.
function zoomed() {
console.info('zoom called');
var t = d3.event.transform;
xScale.domain(t.rescaleX(xScale).domain());
svg.select(".x.axis")
.call(xAxis.tickFormat(d3.timeFormat("%d %b %y")));
}
This is not the complete solution as it just zooms xAxis and not the plot but it might help you in zooming the plot too.
Or you might want to refer: https://bl.ocks.org/deristnochda/1ffe16ccf8bed2035ea5091ab9bb53fb
window.onload = function(){
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
};var width = 600 - margin.left - margin.right;
var height = 270 - margin.top - margin.bottom;
var parseDate = d3.timeFormat("%c").parse;
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x).ticks(5);
var yAxis = d3.axisLeft()
.scale(y).ticks(5);
var valueline = d3.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.close);
});
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Get the data
var data = [{
date: "1 ",
close: "58.13"
}, {
date: "2 ",
close: "53.98"
}, {
date: "3 ",
close: "67.00"
}, {
date: "4 ",
close: "89.70"
}, {
date: "5 ",
close: "99.00"
}, {
date: "6 ",
close: "20.00"
}];
var data2 = [{
date: "1 ",
close: "52.13"
}, {
date: "2 ",
close: "30.98"
}, {
date: "3 ",
close: "36.00"
}, {
date: "4 ",
close: "40.70"
}, {
date: "5 ",
close: "20.00"
}, {
date: "6 ",
close: "96.00"
}];
data.forEach(function (d) {
d.date = d.date;
d.close = +d.close;
});
// scaleLinear the range of the data
x.domain(d3.extent(data, function (d) {
return d.date;
}));
y.domain([0, d3.max(data, function (d) {
return d.close;
})]);
svg.append("path") // Add the valueline path.
.attr("d", valueline(data)).attr("stroke","red");
svg.append("path") // Add the valueline path.
.attr("d", valueline(data2)).attr("stroke","blue");
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
}
I am new to this. I want to add color to the stroke I am using but I am unable to do it. I have added attribute for applying stroke color in the code but it is not working. I want each of the line to be of different color. Please help me with the feasible solution.
Your code seems to work fine. Here is the d3 way of implementation for the same.
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
};
var width = 600 - margin.left - margin.right;
var height = 270 - margin.top - margin.bottom;
var parseDate = d3.timeFormat("%c").parse;
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x).ticks(5);
var yAxis = d3.axisLeft()
.scale(y).ticks(5);
var valueline = d3.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close);
});
var colors = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Get the data
var data = [{
date: "1 ",
close: "58.13"
}, {
date: "2 ",
close: "53.98"
}, {
date: "3 ",
close: "67.00"
}, {
date: "4 ",
close: "89.70"
}, {
date: "5 ",
close: "99.00"
}, {
date: "6 ",
close: "20.00"
}];
var data2 = [{
date: "1 ",
close: "52.13"
}, {
date: "2 ",
close: "30.98"
}, {
date: "3 ",
close: "36.00"
}, {
date: "4 ",
close: "40.70"
}, {
date: "5 ",
close: "20.00"
}, {
date: "6 ",
close: "96.00"
}];
data.forEach(function(d) {
d.date = d.date;
d.close = +d.close;
});
// scaleLinear the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.close;
})]);
svg.selectAll("path.line").data([data, data2])
.enter().
append("path") // Add the valueline path.
.attr("class", "line")
.attr("d", valueline)
.attr("stroke", function(d, i) {
return colors(i);
});
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
path {
fill: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>d
I follow https://bl.ocks.org/nanu146/f48ffc5ec10270f55c9e1fb3da8b38f0 and http://bl.ocks.org/Caged/6476579 and make a Bar Graph with line an tooltip
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: orange;
}
.bar:hover {
fill: orangered ;
}
.x.axis path {
display: none;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<div>
<svg id="graph"></svg>
</div>
<script>
//https://bl.ocks.org/nanu146/f48ffc5ec10270f55c9e1fb3da8b38f0
function getTextWidth(text, fontSize, fontName) {
c = document.createElement("canvas");
ctx = c.getContext("2d");
ctx.font = fontSize + ' ' + fontName;
return ctx.measureText(text).width;
}
function DataSegregator(array, on) {
var SegData;
OrdinalPositionHolder = {
valueOf: function () {
thisObject = this;
keys = Object.keys(thisObject);
keys.splice(keys.indexOf("valueOf"), 1);
keys.splice(keys.indexOf("keys"), 1);
return keys.length == 0 ? -1 : d3.max(keys, function (d) { return thisObject[d] })
}
, keys: function () {
keys = Object.keys(thisObject);
keys.splice(keys.indexOf("valueOf"), 1);
keys.splice(keys.indexOf("keys"), 1);
return keys;
}
}
array[0].map(function (d) { return d[on] }).forEach(function (b) {
value = OrdinalPositionHolder.valueOf();
OrdinalPositionHolder[b] = OrdinalPositionHolder > -1 ? ++value : 0;
})
SegData = OrdinalPositionHolder.keys().map(function () {
return [];
});
array.forEach(function (d) {
d.forEach(function (b) {
SegData[OrdinalPositionHolder[b[on]]].push(b);
})
});
return SegData;
}
Data = [
{ Date: "1", Categories: [{ Name: "Test Exam Jan", Value: 10 }], LineCategory: [{ Name: "Line1", Value: 69 }] },
{ Date: "2", Categories: [{ Name: "Test Exam Feb", Value: 1 }], LineCategory: [{ Name: "Line1", Value: 89 }] },
{ Date: "3", Categories: [{ Name: "Test Exam March", Value: 1 }], LineCategory: [{ Name: "Line1", Value: 72 }] },
{ Date: "4", Categories: [{ Name: "Test Exam 1", Value: 1 }], LineCategory: [{ Name: "Line1", Value: 75 }] },
{ Date: "5", Categories: [{ Name: "Test Exam 2", Value: 5 }], LineCategory: [{ Name: "Line1", Value: 52 }] },
{ Date: "6", Categories: [{ Name: "Test Exam 3", Value: 3 }], LineCategory: [{ Name: "Line1", Value: 40 }] },
{ Date: "7", Categories: [{ Name: "Test Exam 4", Value: 12 }], LineCategory: [{ Name: "Line1", Value: 37 }] },
{ Date: "8", Categories: [{ Name: "Test Exam 5", Value: 5 }], LineCategory: [{ Name: "Line1", Value: 68 }] },
{ Date: "9", Categories: [{ Name: "Test Exam 6", Value: 3 }], LineCategory: [{ Name: "Line1", Value: 92 }] },
{ Date: "10", Categories: [{ Name: "Test Exam 7", Value: 7 }], LineCategory: [{ Name: "Line1", Value: 95 }] },
{ Date: "11", Categories: [{ Name: "Test Exam 8", Value: 2 }], LineCategory: [{ Name: "Line1", Value: 55 }] },
{ Date: "12", Categories: [{ Name: "Test Exam 9", Value: 9 }], LineCategory: [{ Name: "Line1", Value: 50 }] },
{ Date: "13", Categories: [{ Name: "Test Exam 10",Value: 1 }], LineCategory: [{ Name: "Line1", Value: 25 }] },
{ Date: "14", Categories: [{ Name: "Test Exam 11",Value: 4 }], LineCategory: [{ Name: "Line1", Value: 99 }] },
{ Date: "15", Categories: [{ Name: "Test Exam 12",Value: 7 }], LineCategory: [{ Name: "Line1", Value: 82 }] },
{ Date: "16", Categories: [{ Name: "Test Exam 13",Value: 5 }], LineCategory: [{ Name: "Line1", Value: 32 }] },
]
var margin = { top: 20, right: 30, bottom: 60, left: 40 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var textWidthHolder = 0;
/// Adding Date in LineCategory
Data.forEach(function (d) {
d.LineCategory.forEach(function (b) {
b.Date = d.Date;
})
});
var Categories = new Array();
// Extension method declaration
Categories.pro
var Data;
var ageNames;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width],.1);
var XLine = d3.scale.ordinal()
.rangeRoundPoints([0, width], .5);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var YLine = d3.scale.linear().range([height, 0])
.domain([0, d3.max(Data, function (d) { return d3.max(d.LineCategory, function (b) { return b.Value }) })]);
/*var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);*/
var line = d3.svg.line().x(function (d) {
return x0(d.Date) + x0.rangeBand() / 2;
}).y(function (d) { return YLine(d.Value) });
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var YLeftAxis = d3.svg.axis().scale(YLine).orient("right").tickFormat(d3.format(".2s"));
console.log(YLeftAxis)
var svg = d3.select("#graph")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Frequency:</strong> <span style='color:red'>Test</span>";
})
svg.call(tip);
// Bar Data categories
Data.forEach(function (d) {
d.Categories.forEach(function (b) {
if (Categories.findIndex(function (c) { return c.Name===b.Name}) == -1) {
b.Type = "bar";
//console.log(JSON.stringify(b))
Categories.push(b)
}
})
});
// Line Data categories
Data.forEach(function (d) {
d.LineCategory.forEach(function (b) {
if (Categories.findIndex(function (c) { return c.Name === b.Name }) == -1) {
b.Type = "line";
//console.log(JSON.stringify(b))
Categories.push(b)
}
})
});
// Processing Line data
lineData = DataSegregator(Data.map(function (d) { return d.LineCategory }), "Name");
// Line Coloring
LineColor = d3.scale.ordinal();
LineColor.domain(Categories.filter(function (d) { return d.Type == "line" }).map(function (d) { return d.Name }));
LineColor.range(["#d40606", "#06bf00", "#98bdc5", "#671919", "#0b172b"])
x0.domain(Data.map(function (d) { return d.Date; }));
XLine.domain(Data.map(function (d) { return d.Date; }));
x1.domain(Categories.filter(function (d) { return d.Type == "bar" }).map(function (d) { return d.Name})).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(Data, function (d) { return d3.max(d.Categories, function (d) { return d.Value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + (width) + ",0)")
.call(YLeftAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -10)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Percent");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(Data)
.enter().append("g")
.attr("class", "state")
.attr("transform", function (d) { return "translate(" + x0(d.Date) + ",0)"; });
state.selectAll(".bar")
.data(function (d) { return d.Categories; })
.enter().append("rect")
//.attr("width", x1.rangeBand())
.attr("class", "bar")
.attr("width", x0.rangeBand())
.attr("x", function (d) { return x1(d.Name); })
.attr("y", function (d) { return y(d.Value); })
.attr("height", function (d) { return height - y(d.Value); })
//.style("fill", function (d) { return color(d.Name); })
.on("click",function(d){console.log(d)})
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.transition().delay(500).attrTween("height", function (d) {
var i = d3.interpolate(0, height - y(d.Value));
return function (t)
{
return i(t);
}
});
// drawaing lines
svg.selectAll(".lines").data(lineData).enter().append("g").attr("class", "line")
.each(function (d) {
Name=d[0].Name
d3.select(this).append("path").attr("d", function (b) { return line(b) }).style({ "stroke-width": "2px", "fill": "none" }).style("stroke", LineColor(Name)).transition().duration(1500);
})
// Legends
/* var LegendHolder = svg.append("g").attr("class", "legendHolder");
var legend = LegendHolder.selectAll(".legend")
.data(Categories.map(function (d) { return {"Name":d.Name,"Type":d.Type}}))
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) { return "translate(0," +( height+ margin.bottom/2 )+ ")"; })
.each(function (d,i) {
// Legend Symbols
d3.select(this).append("rect")
.attr("width", function () { return 18 })
.attr("x", function (b) {
left = (i+1) * 15 + i * 18 + i * 5 + textWidthHolder;
return left;
})
.attr("y", function (b) { return b.Type == 'bar'?0:7})
.attr("height", function (b) { return b.Type== 'bar'? 18:5 })
.style("fill", function (b) { return b.Type == 'bar' ? color(d.Name) : LineColor(d.Name) });
// Legend Text
d3.select(this).append("text")
.attr("x", function (b) {
left = (i+1) * 15 + (i+1) * 18 + (i + 1) * 5 + textWidthHolder;
return left;
})
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(d.Name);
textWidthHolder += getTextWidth(d.Name, "10px", "calibri");
})
;*/
// Legend Placing
d3.select(".legendHolder").attr("transform", function (d) {
thisWidth = d3.select(this).node().getBBox().width;
return "translate(" + ((width) / 2 - thisWidth / 2) + ",0)";
})
</script>
</body>
If you run above code you will see a barchart like bellow image
the problem is last bar overlap with right axis .which I unable to fix .
any body have any suggestion to fix .please help .
You are not correctly positioning your bars regarding the x coordinate.
Since your groups are already translated...
.attr("transform", function (d) {
return "translate(" + x0(d.Date) + ",0)";
});
... this line:
.attr("x", function (d) { return x1(d.Name); })
Should be simply:
.attr("x", 0)
Here is a fiddle with that change only: https://jsfiddle.net/6gpwmups/
I am using D3 to create two level grouped category bar chart with brush functionality on x axis.But X axis line not displayed in FireFox even though i didn't add any css related to hide x axis.
X axis line appeared in Chrome and IE.
NOTE:here also if we click on Full Page x axis line display.else it doesn't display.
// Code goes here
var barsData = [
{
"value":100,
"key":"03-08-2016,0",
"secondKey":"Windows 7/Chrome49"
},
{
"value":40,
"key":"04-08-2016,1",
"secondKey":"Windows 7/Chrome49"
},
{
"value":20,
"key":"05-08-2016,2",
"secondKey":"Windows 7/Chrome49"
},
{
"value":100,
"key":"03-08-2016,3",
"secondKey":"Windows 7/Chrome50"
},
{
"value":27,
"key":"04-08-2016,4",
"secondKey":"Windows 7/Chrome50"
},
{
"value":57,
"key":"05-08-2016,5",
"secondKey":"Windows 7/Chrome50"
},
{
"value":40,
"key":"04-08-2016,6",
"secondKey":"Windows 7/Firefox44"
},
{
"value":60,
"key":"05-08-2016,7",
"secondKey":"Windows 7/Firefox44"
},
{
"value":50,
"key":"04-08-2016,8",
"secondKey":"Windows 7/Chrome47"
},
{
"value":40,
"key":"05-08-2016,9",
"secondKey":"Windows 7/Chrome47"
},
{
"value":80,
"key":"04-08-2016,10",
"secondKey":"Windows 7/Firefox45"
},
{
"value":60,
"key":"05-08-2016,11",
"secondKey":"Windows 7/Firefox45"
},
{
"value":0,
"key":"04-08-2016,12",
"secondKey":"Windows 7/IE10"
},
{
"value":40,
"key":"05-08-2016,13",
"secondKey":"Windows 7/IE10"
},
{
"value":50,
"key":"04-08-2016,14",
"secondKey":"Windows 7/Firefox42"
}
];
var osLevelData = [
{
"key":"Windows 7/Chrome49",
"values":[
{
"value":100,
"key":"03-08-2016,0",
"secondKey":"Windows 7/Chrome49"
},
{
"value":40,
"key":"04-08-2016,1",
"secondKey":"Windows 7/Chrome49"
},
{
"value":20,
"key":"05-08-2016,2",
"secondKey":"Windows 7/Chrome49"
}
],
"centerBarPosVal":"03-08-2016,0",
"lastBarPosVal":"05-08-2016,2"
},
{
"key":"Windows 7/Chrome50",
"values":[
{
"value":100,
"key":"03-08-2016,3",
"secondKey":"Windows 7/Chrome50"
},
{
"value":27,
"key":"04-08-2016,4",
"secondKey":"Windows 7/Chrome50"
},
{
"value":57,
"key":"05-08-2016,5",
"secondKey":"Windows 7/Chrome50"
}
],
"centerBarPosVal":"03-08-2016,3",
"lastBarPosVal":"05-08-2016,5"
},
{
"key":"Windows 7/Firefox44",
"values":[
{
"value":40,
"key":"04-08-2016,6",
"secondKey":"Windows 7/Firefox44"
},
{
"value":60,
"key":"05-08-2016,7",
"secondKey":"Windows 7/Firefox44"
}
],
"centerBarPosVal":"04-08-2016,6",
"lastBarPosVal":"05-08-2016,7"
},
{
"key":"Windows 7/Chrome47",
"values":[
{
"value":50,
"key":"04-08-2016,8",
"secondKey":"Windows 7/Chrome47"
},
{
"value":40,
"key":"05-08-2016,9",
"secondKey":"Windows 7/Chrome47"
}
],
"centerBarPosVal":"04-08-2016,8",
"lastBarPosVal":"05-08-2016,9"
},
{
"key":"Windows 7/Firefox45",
"values":[
{
"value":80,
"key":"04-08-2016,10",
"secondKey":"Windows 7/Firefox45"
},
{
"value":60,
"key":"05-08-2016,11",
"secondKey":"Windows 7/Firefox45"
}
],
"centerBarPosVal":"04-08-2016,10",
"lastBarPosVal":"05-08-2016,11"
},
{
"key":"Windows 7/IE10",
"values":[
{
"value":0,
"key":"04-08-2016,12",
"secondKey":"Windows 7/IE10"
},
{
"value":40,
"key":"05-08-2016,13",
"secondKey":"Windows 7/IE10"
}
],
"centerBarPosVal":"04-08-2016,12",
"lastBarPosVal":"05-08-2016,13"
},
{
"key":"Windows 7/Firefox42",
"values":[
{
"value":50,
"key":"04-08-2016,14",
"secondKey":"Windows 7/Firefox42"
}
],
"centerBarPosVal":"04-08-2016,14",
"lastBarPosVal":"04-08-2016,14"
}
];
var barColor = "#4A7B9D";
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 860 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var min_margin = {
top: height,
right: margin.right + 10,
bottom: margin.bottom,
left: margin.left + 10
},
min_height = 10,
min_width = 860 - min_margin.left - min_margin.right;
//first scale
var x = d3.scale.ordinal().rangeRoundBands([0, width], .2);
//second scale
var groupx = d3.scale.ordinal().rangeRoundBands([0, width], .2);
//scroll scale
var min_x = d3.scale.ordinal().rangeRoundBands([0, width], .2);
//y scale
var y = d3.scale.linear().range([height, 0]);
//Add domain for X scale
x.domain(barsData.map(function(d) {
return d.key;
}));
//Add domain for X scale
groupx.domain(osLevelData.map(function(d) {
return d.key;
}));
//scroll domain
min_x.domain(barsData.map(function(d) {
return d.key;
}));
//domain Y
y.domain([0, d3.max(barsData, function(d) {
return d.value;
})]);
//x axis
var xAxis = d3.svg.axis()
.scale(x)
.tickFormat(function(d){
return d.split(",")[0];
})
.orient("bottom");
//group axis
var groupAxis = d3.svg.axis()
.scale(groupx)
.tickFormat(function(d){
return d;
})
.orient("bottom");
//scroll axis
var min_xAxis = d3.svg.axis()
.scale(min_x)
.tickFormat(function(d){
return d;
})
.orient("bottom");
var main_xZoom = d3.scale.linear()
.range([0, width])
.domain([0, width]);
// y axis
var yAxis = d3.svg.axis()
.scale(y)
.tickFormat(function(d){
return d;
})
.orient("left")
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", 800 + margin.top + margin.bottom);
//main g
var main = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
main.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height + min_height + margin.bottom);
// Add the x axis DOM elements
var xDOM = main.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height + min_height) + ")")
.attr("clip-path", "url(#clip)")
.call(xAxis)
//.selectAll(".tick text")
//.call(wrap, x.rangeBand());
xDOM.selectAll(".tick").append("line")
.attr("class","groupline")
.attr("y2",30)
.attr("transform", function(d,index) {
/*var position;
osLevelData.forEach(function(ddd){
if(ddd.key === d){
position = ddd.lastBarPosVal;
}
})*/
return "translate(" + (x.rangeBand()/2) + ",0)";
//console.log("d ..",d);
});
// Add the group axis DOM elements
var groupDOM = main.append("g")
.attr("class", "x1 axis")
.attr("transform", "translate(0," + (height + min_height + 30) + ")")
.attr("clip-path", "url(#clip)")
.call(groupAxis)
.selectAll(".tick")
.attr("transform", function(d) {
var centerPos;
osLevelData.forEach(function(ddd){
if(ddd.key === d){
centerPos = ddd.centerBarPosVal;
}
})
return "translate(" + (x(centerPos)+(x.rangeBand()/2)) + ",0)";
});
//.selectAll(".tick text")
//.call(wrap, groupx.rangeBand());
groupDOM.append("line")
.attr("class","groupline")
.attr("y2",30)
.attr("transform", function(d) {
var lastPos;
var centerPos;
osLevelData.forEach(function(ddd){
if(ddd.key === d){
lastPos = ddd.lastBarPosVal;
centerPos = ddd.centerBarPosVal;
}
})
return "translate(" + ((x(lastPos)+x.rangeBand()) - (x(centerPos)+(x.rangeBand()/2))) + ",0)";
});
//scroll DOM element
var mini_x_append = main.append("g")
.attr("transform", "translate(0," + (margin.top + height + 60) + ")")
.attr("width", min_width);
main.append("g")
.attr("class", "y axis")
.call(yAxis);
//create group with all bars
main.append("g")
.attr("clip-path", "url(#clip)")
.selectAll(".rect")
.data(barsData)
.enter().append("rect")
.attr("class", "rect")
.attr("x", function(d) {
return x(d.key);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.attr("fill",barColor);
//To display bar value on bars
main.selectAll("text.bar")
.data(barsData)
.enter().append("text")
.attr("class", "bar")
.attr("text-anchor", "middle")
.attr("x", function(d) { return x(d.key) + x.rangeBand()/2; })
.attr("y", function(d) { return y(d.value) - 5; })
.text(function(d) { return d.value+"%"; });
var xBrush = d3.svg.brush().x(min_x).on("brush", xBrushed);
//d3.svg.brush().x(groupx).on("brush", xBrushed);
var x_arc = d3.svg.arc()
.outerRadius(min_height / 2)
.startAngle(0)
.endAngle(function(d, i) {
return i ? -Math.PI : Math.PI;
});
var brush_x_grab = mini_x_append.append("g")
.attr("class", "x brush")
.call(xBrush);
brush_x_grab.selectAll(".resize").append("path")
.attr("transform", "translate(0," + min_height / 2 + ")")
.attr("d", x_arc)
.attr("fill","#FF0000");
brush_x_grab.selectAll("rect").attr("height", min_height).style("visibility","visible").attr("fill","#D3D3D3");
// Called to re-draw the bars on the main chart when the brush on the x axis
// has been altered.
function xBrushed() {
var originalRange = main_xZoom.range();
main_xZoom.domain(xBrush.empty() ? originalRange : xBrush.extent());
x.rangeRoundBands([main_xZoom(originalRange[0]), main_xZoom(originalRange[1])], .2);
groupx.rangeRoundBands([main_xZoom(originalRange[0]), main_xZoom(originalRange[1])], .2);
main.selectAll(".rect")
.data(barsData)
.attr("width", function(d) {
return x.rangeBand();
})
.attr("x", function(d) {
return x(d.key);
});
main.selectAll(".bar")
.data(barsData)
.attr("x", function(d) { return x(d.key) + x.rangeBand()/2; })
.attr("y", function(d) { return y(d.value) - 5; })
.text(function(d) { return d.value+"%"; });
main.select("g.x.axis").call(xAxis).selectAll(".tick .groupline")
.attr("transform", function(d,index) {
return "translate(" + (x.rangeBand()/2) + ",0)";
});
main.select("g.x1.axis").call(groupAxis).selectAll(".tick")
.attr("transform", function(d) {
var centerPos;
osLevelData.forEach(function(ddd){
if(ddd.key === d){
centerPos = ddd.centerBarPosVal;
}
})
return "translate(" + (x(centerPos)+(x.rangeBand()/2)) + ",0)";
})
.selectAll(".tick .groupline")
.attr("transform", function(d) {
var centerPos;
var lastPos;
osLevelData.forEach(function(ddd){
if(ddd.key === d){
lastPos = ddd.lastBarPosVal;
centerPos = ddd.centerBarPosVal;
}
})
return "translate(" + ((x(lastPos)+x.rangeBand()) - (x(centerPos)+(x.rangeBand()/2))) + ",0)";
})
//main.selectAll(
};
// This comes from the example at http://bl.ocks.org/mbostock/7555321
// for wrapping long axis tick labels
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
};
// Set the initial brush selections.
// svg.select(".x.brush").call(xBrush.extent(main_xZoom.domain()));
svg.select(".x.brush").call(xBrush.extent([0, 220]));
//svg.select(".y.brush").call(yBrush.extent(mini_y0.domain()));
// Forces a refresh of the brushes and main chart based
// on the selected extents.
xBrushed();
//yBrushed();
function type(d) {
d.frequency = +d.frequency;
return d;
}
/* Styles go here */
g.axis path,
g.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
g.brush rect.extent {
fill-opacity: 0.5;
fill:#FF0000;
}
.resize path {
fill-opacity: 0.2;
}
.bar {
fill: steelblue;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
</body>