Position an inset legend at the top-center - javascript

According to the C3 documentation, legend.inset.postition only supports top-left, top-right, bottom-left and bottom-right positions. I would like to have my legend positioned at the top-center.
Here is the JS that creates the plot:
var chart = c3.generate({
data: {
columns: [
['data1', 100, 200, 50, 300, 200, 50, 250, 100, 200, 400, 50],
['data2', 400, 500, 250, null, 300, 50, 200, 450, 300, 300, 100]
],
},
legend: {
position: 'inset',
inset: {
anchor: 'top-left', // how do I implement 'top-center'?
x: 40,
y: 2,
step: 1
}
}
});
I have attempted to re-position the element after the chart has been rendered via figuring out its current position. However, none of the SVG elements appear to have attributes that enable this kind of introspection:
var legend = d3.select(".c3-legend-background");
console.log("style[width]: " + legend.style("width")); // style[width]: auto
console.log("attr[x]: " + legend.attr("x")); // attr[x]: null
console.log("attr[y]: " + legend.attr("y")); // attr[y]: null

Wouldn't you just set the x value of the inset object to the correct value to get it to center across the top?
This would depend on your wide your chart if and how wide your legend is. So the calculation would be (ChartWidth - LegendWidth) / 2.
The legend object is then something like:
legend: {
position:'inset',
inset: {
anchor: 'top-left',
x: 200 // put the result of your calculation here
}
}
Here's a rough example: http://jsfiddle.net/jrdsxvys/11/

Related

C3.js - Adding Spacing between the X-axis and Legend in Bar Graphs

I have a C3.js bar graph and I am trying to add some spacing in between the x-axis and legend in order to avoid the following visual unpleasantry:
I have already tried to do the following and it has had no effect on the graph:
#stacked_bar_chart_container .c3-axis-x {
margin-bottom: 5px;
}
I have even tried to do the same thing with .c3-legend-item but with a margin-top: 5px; instead, but it has still had no effect.
I would also like to note that by removing #stacked_bar_chart_container (the div container that this graph is binded to) there was still no effect either.
You can adjust the space between the chart and bottom legend by using padding: {bottom: 20}, which will add additional 20px between chart and legend. If you place the legend on the right side, you can use padding: {right: 20}.
Because padding will make your chart smaller, you may want to adjust the height or width of your chart by using size: {height: 480} or size: {width: 640}, respectively.
I have created a fiddle example http://jsfiddle.net/6jztrbk7/
This is the example JavaScript code:
var chart = c3.generate({
data: {
columns: [
['data1', -30, 200, 200, 400, -150, 250],
['data2', 130, 100, -100, 200, -150, 50],
['data3', -230, 200, 200, -300, 250, 250],
type: 'bar',
groups: [
['data1', 'data2']
]
},
grid: {
y: {
lines: [{value:0}]
}
},
legend: {
position: 'bottom'
},
size: {
height: 300 //adjust chart height
},
padding: {
bottom: 20 //adjust chart padding bottom
}
});

How to highlight only one datapoint in c3.js line graph?

Using point show as false hides all the data points.
But what to do if I want to hide all but one datapoint.
For Example,
var chart = c3.generate({
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
});
In the above line chart, how to highlight the data point where value is 100 and hide every other data point?
In C3, those circles have a class named c3-circle. So, using a D3 selection, you can set the opacity based on the bound datum:
var circles = d3.selectAll(".c3-circle")
.style("opacity", function(d){
return d.value === 100 ? 1 : 0;
})
Thus, only the circle corresponding to 100 is visible.
Here is the demo:
var chart = c3.generate({
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
});
var circles = d3.selectAll(".c3-circle").style("opacity", function(d){
return d.value === 100 ? 1 : 0;
})
<script src="https://d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://rawgit.com/masayuki0812/c3/master/c3.css">
<script src="https://rawgit.com/masayuki0812/c3/master/c3.js"></script>
<div id="chart"></div>

C3js Stacked Bar Chart with a hidden bar

Scenario: I need to show a bar chart about payment details, how much the customers paid for a product using Card, Cheque or Cash. And also need to display the total value in a stacked bar chart.
var chart = c3.generate({
data: {
columns: [
['Cash', 30, 200, 100, 400, 150, 250],
['Card', 130, 100, 140, 200, 150, 50],
['Total', 160,300,240,600,300,300]
],
groups:[['Cash','Card']],
type: 'bar'
}
});
Output:
I need to show the Total value but not the bar and the legend. How can do this? Any advice would be helpful. Thank you.
You don't need the total dataset, you can alter the tooltip contents to calculate it on the fly (adapting the technique found in the answer here -> https://stackoverflow.com/a/36789099/368214)
var chart = c3.generate({
data: {
columns: [
['Cash', 30, 200, 100, 400, 150, 250],
['Card', 130, 100, 140, 200, 150, 50],
],
groups:[['Cash','Card']],
type: 'bar'
},
tooltip : {
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
var total = d.reduce (function(subTotal,b) { return subTotal + b.value; }, 0);
d.push ({value: total, id: "Total", name: "Total", x:d[0].x , index:d[0].index});
return this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color);
}
}
});
The total value is calculated using .reduce to sum all the other values at that point. Then make a new data point for the tooltip called 'Total' using that value and pass it to the default renderer for drawing (this.getTooltipContent)
http://jsfiddle.net/vz1rwvwn/

Set tooltip position on the last value on the graph

There are some way to set the position for the tooltip in c3js, because the tooltip is always at the right side of the vertical line, but when the cursor is on the last value then it changes position towards left side or state in the graph without leaving it.
I know that this is normal tooltip behavior on c3js but I want that the tooltip to keep its position on the right side for the last value
you can see the example on this link
var chart = c3.generate({
data: {
columns: [
['data1', 300, 350, 300, 0, 0, 0],
['data2', 130, 100, 140, 200, 150, 50]
],
types: {
data1: 'area',
data2: 'area-spline'
}
},
axis: {
y: {
padding: {bottom: 0},
min: 0
},
x: {
padding: {left: 0},
min: 0,
show: false
}
}
});
any ideas?
You can set the tooltip position using this code:
http://c3js.org/reference.html#tooltip-position
tooltip: {
position: function (data, width, height, element) {
var top = d3.mouse(element)[1] + 15;
return {top: top, left: parseInt(element.getAttribute('x')) + parseInt(element.getAttribute('width'))}
}
}
However how it works when the tooltip pokes out the side of the chart I don't know, it may just be cutoff. You could add a margin of 100px to the right if needed I suppose but that makes your chart smaller.

Modify Donut Chart Slices in C3/D3

I have created a simple donut chart using c3.js. Here is the FIDDLE.
If you hover over a slice of the Donut it will stick out. I was wondering if it is possible for the slice to stick out by default without hovering over.
For example i want slice A, slice B, and C to stickout by default How can I do that?
Here is my Code
var currentSlice;
var chart = c3.generate({
data: {
x: 'x',
columns: [
['x', '2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04', '2013-01-05', '2013-01-06'],
['A', 30, 200, 100, 400, 150, 250],
['B', 130, 100, 140, 200, 150, 50],
['C', 50, 100, 130, 240, 200, 150],
['D', 130, 100, 140, 200, 150, 50],
['E', 130, 150, 200, 300, 200, 100]
],
type: 'donut',
onclick: function (e) {
},
onmouseover: function (d, i) {
if(currentSlice !== void 0) {
'currentSlice'.attr("transform","scale(1)")
}
currentSlice = d3.select(i).attr("transform", "scale(1.1)");
},
onmouseout: function (d, i) {
}
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d',
centered: true,
position: 'inner-right'
}
}
},
bindto: '#dash',
bar: {
width: {
ratio: 0.5 // this makes bar width 50% of length between ticks
}
},
pie: {
expand: true,
},
tooltip: {
grouped: false,
contents: function (data, defaultTitleFormat, defaultValueFormat, color) {
// console.log("Containt");
// console.log(data, defaultTitleFormat, defaultValueFormat, color);
return "<p style='border:1px solid red;'>" + data[0].value + "</p>";
}
}
});
c3js has two options, but both require a slight hack with 'setTimeout' to force our default scaling to happen after rendering and animation occur.
The onrendered function is available to set within the c3config object that one initializes the chart with. This function is triggered after a redraw is triggered but before visual rendering happens in the DOM. However, there is a hack to use setTimeout since it will create a separate callstack that will execute after the current callstack which in c3 happens to include redrawing the graph. (explanation of setTimeout to force logic to run after current callstack executes)
The load function exposes a done callback that is triggered after the elements are rendered to the DOM but before the animation finishes. So if one sets the initial scale transform in done, if the animations triggered by load are using the scale transform (which loading a pie chart appears to do), then the last keyframe of the animation will overwrite your modified scale back to scale(1). However, we can similarly use setTimeout to run our code after the current callstack (which includes animation) executes.
In exploring this, I created a generalized form of rby's answer, and I offer two alternative paths to setting default scales through the onrendered and done functions exposed in c3. (Fiddle):
var selected = ['A', 'B', 'C'];
var _ARC = '.c3-arc';
var _SCALING = '1.1';
function getCurrentlySelected() {
var _PREFIX = _ARC + '-';
return d3.selectAll(_PREFIX + selected.join(', ' + _PREFIX));
}
Within c3config object and onrendered through initialization:
var chart = c3.generate({
bindto: '#chart',
data: { ... },
onrendered: function() {
setTimeout(function() {
if (selected.length > 0) {
getCurrentlySelected().attr('transform', 'scale(' + _SCALING + ')');
}
}); // Notice we don't need a delay, just taking advantage to force our logic to happen after current callstack is executed
}
});
Also possible to use load with done after initialization:
chart.load({
columns: [
['A', 30, 200, 100, 400, 150, 250],
['B', 130, 100, 140, 200, 150, 50],
['C', 50, 100, 130, 240, 200, 150],
['D', 130, 100, 140, 200, 150, 50],
['E', 130, 150, 200, 300, 200, 100]
],
done: function() {
setTimeout(function() {
if (selected.length > 0) {
getCurrentlySelected().attr('transform', 'scale(' + _SCALING + ')');
}
}) // Notice we don't need a delay, just taking advantage to force our logic to happen after current callstack is executed
}
});
You can use setTimeout() to scale specific slices, once the chart is rendered. Here's one way:
setTimeout( function() {
d3.selectAll('.c3-arc-A, .c3-arc-B, .c3-arc-C').attr("transform", "scale(1.2)");
}, 5);
Place this call after your c3.generate() call.

Categories