So I've got a pretty small div, in which I gotta get a small timeline chart shoved in. The current problem is, that due to the chart being perfectly centered, I have a lot of whitespace I could really use, to make the actual data and chart much larger.:
I think showing the parent div shouldn't matter(because changing around these sizes doesn't really affect the chart itself). It feels like there's some option in the configuration that I'm missing. I've tried setting offsetY and even offsetX to negative values, because I was desperate, but to no avail.
<html>
<div id="chartTarget" style="height: 100; width: 100%;">
<canvas id="chartCanvas" height="100"></canvas>
<div style="opacity:0;" class="chartTooltip center">
</div>
</div>
</html>
Relevant code is pretty much straight up copied from the example, but since I'm bound to get questions about it, here goes:
var options = {
series: [
{
name: 'Pending',
data: [
{
x: 'Trip',
y: [
new Date('2021-04-21T03:24:00').getTime(),
new Date('2021-04-21T04:24:00').getTime()
]
}
]
},
{
name: 'Active',
data: [
{
x: 'Trip',
y: [
new Date('2021-04-21T05:24:00').getTime(),
new Date('2021-04-21T08:24:00').getTime()
]
}
]
},
{
name: 'Deleted',
data: [
{
x: 'Trip',
y: [
new Date('2021-04-21T11:24:00').getTime(),
new Date('2021-04-21T12:24:00').getTime()
]
}
]
}
],
chart: {
height: 75,
width: 250,
type: 'rangeBar',
offsetY: -25,
toolbar: {
show: false,
tools: {
download: false,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: true,
reset: true
}
},
events: {
dataPointMouseEnter: function (event, chartContext, config) {
config.w.config.chart.toolbar.show = false;
},
dataPointMouseLeave: function (event, chartContext, config) {
config.w.config.chart.toolbar.show = false;
}
}
},
plotOptions: {
bar: {
horizontal: true,
barHeight: '50%',
rangeBarGroupRows: true
}
},
colors: [
"#008FFB", "#00E396", "#FEB019", "#FF4560", "#775DD0",
"#3F51B5", "#546E7A", "#D4526E", "#8D5B4C", "#F86624",
"#D7263D", "#1B998B", "#2E294E", "#F46036", "#E2C044"
],
fill: {
type: 'solid'
},
xaxis: {
type: 'datetime',
labels: {
maxHeight: 25,
style: {
fontSize: '9px'
},
offsetY: -8
},
tooltip: {
enabled: false
}
},
yaxis: {
show: false,
lines: {
show: true
}
},
legend: {
show: false,
position: 'top'
}
};
var chart = new ApexCharts(document.querySelector("#chartTarget"), options);
chart.render();
I need the label, so things like sparkline aren't useful.
A negative offset at the chart level seems to have fixed for my needs.
chart.offsetY: -25
I assume everyone else can play with negative offset values, where available for more fine tuning.
Related
I am trying to make a chart which has years along the x-axis and dollar amounts along the y-axis. I finally got close to what I'm looking for, but I found that because the x coordinates are numbers, ChartJS is putting commas in them which looks really strange for years.
After some digging, I used the callbacks. options.plugin.tooltip.callbacks.label worked to let me remove commas in the tooltips, but when I use options.scales.x[0].ticks.callback to try to fix the labels on the bottom, not only does it not work, but I don't see the console.log statement in their ever being printed so it seems it's not even calling the callback. I've tried several variations of how to do the callback based on what I found online and on Stack Overflow which I think correspond to the different ways ChartJS did this in different versions. (I'm on version 3.5.1.)
Then, I realized that... none of the options under options.scales appear to have any effect. I change the min, the title, the tick settings (color to red, callback, etc.) and it has no effect. (This also explains why I was having trouble when using the line chart and had to switch to scatter; apparently type: 'linear' wasn't being picked up nor did it do anything different when I set it to type: 'date' or whatever the exact working was for that.)
Meanwhile, the other options like options.showLine or options.elements do have an effect and I'm seeing the chart and not getting any errors in the console. So, it is picking up the options, just ignoring everything I have in options.scales.
Here is the relevant code:
// Sample data added to make this example self-contained
// This is my internal data format
let data = {
"Series1": [ {x: 2001, y: 100 }, {x: 2002, y: 110 }, {x: 2003, y: 107 }, ],
"Series2": [ {x: 2001, y: 107 }, {x: 2002, y: 102 }, {x: 2004, y: 95 }, ],
}
// Define data //////////////////////////////////////////////////////
// I convert data to format ChartJS wants and add a few options
let datasets = [];
for(let label in data) {
let c = colorIterator.next().value
datasets.push({
label: label,
data: data[label],
backgroundColor: c,
borderColor: c,
});
}
// Define options //////////////////////////////////////////////////////
let chartConfig = {
type: 'scatter',
data: { datasets: datasets, },
options: {
title: { display: false },
indexAxis: 'x', responsive: true, maintainAspectRatio: false,
showLine: true,
elements: {
line: { display: true, tension: 0, borderWidth: 1, fill: false, },
point: { radius: 3 }
},
interaction: { mode: 'x', },
scales: {
x: [{
type: 'linear',
min: 1995, max: (new Date()).getFullYear()+1, stepSize: 1,
title: { display: true, text: 'Year' },
ticks: {
display: true,
major: { enabled: true },
color: 'red',
callback: function(value, index, ticks) {
console.log(value);
return Chart.Ticks.formatters.numeric.apply(this, [value, index, ticks])
.replace(",","");
}
}
}],
y: [{
title: { display: true, text: '$' },
ticks: {
display: true,
color: 'red',
},
}],
},
plugins: {
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if(label) {
let x = context.label.replace(",","");
let y = context.formattedValue;
return 'Year ' + x + ' "' + label + '": $' + y;
} else { return 'x'; }
},
},
},
},
}
};
// MAKE CHART //////////////////////////////////////////////////////
let mainChart = new Chart(document.getElementById(c.id), chartConfig);
As described in the docs the scales are not arrays. All the scales are objects in the scale object.
So you will need to change your code to this:
options: {
scales: {
x: {
// x options
},
y: {
// y options
},
}
}
I'm creating gauge charts using c3.js. I'm setting the height and width both to 75 because that's the right size of the gauge that I'm wanting, however when they get generated, there's always extra whitespace in the container that's messing me up.
I really want the svg that gets created to have a height of 60 in order to move the label up properly. The problem, is that when I set the height/width of the chart to 60, the size of the gauge itself gets way too small because of this extra whitespace.
I've tried setting the padding of everything that I know of to 0. I've searched through the documentation, there's always a chance that I've overlooked something. I can always try to do some hacky css to get around it, but before I do that, I'd like to change something in the configuration if I can.
Essentially, I want the chart to take up the full size that I specify. It seems that the legend, that I've specified to not show, is still taking up space that the chart should use.
http://jsfiddle.net/kLsox4ya/1/
<div class='row'>
<div class="col-12" id="chart"></div>
<p class="col-12 f-small">PERFECT</p>
</div>
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [['data', 0]],
type: 'gauge'
},
gauge: {
fullCircle: true,
startingAngle: 2 * Math.PI,
width: 3,
expand: false,
label: {
show: false
}
},
size: {
height: 75,
width: 75
},
legend: {
show: false
},
interaction: {
enabled: false
}
});
You are going to have a hard time getting that much control over c3. It's doing a lot under the hood to calculate positions for axis, legends, etc... that you aren't even using.
I think you have two options:
Code it yourself using straight d3
Resort to a little hackery. For instance here, I've manually adjust the height after it renders.
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [['data', 90]],
type: 'gauge'
},
tooltip: {
show: false
},
color: {
pattern: ['#565656', '#cfd628', '#e8b532', '#28d632'],
threshold: {
values: [40, 80, 90, 100]
}
},
gauge: {
fullCircle: true,
startingAngle: 2 * Math.PI,
width: 3,
label: {
format: function (value, ratio) {
return '';
},
extents: function (value) {
return '';
}
}
},
size: {
height: 75,
width: 75
},
legend: {
show: false
},
interaction: {
enabled: false
},
axis: {
x: {
show: false
},
y: {
show: false
}
},
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
onrendered: function(){
this.svg.attr('height', 55);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.6.12/c3.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.6.12/c3.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class='row'>
<div class="col-12" id="chart"></div>
<p class="col-12 f-small">PERFECT</p>
</div>
I have used a Bubble Chart on Chart.js to create sliders to show comparable performance and they currently look a bit like this:
What am I trying to do
I want to add data labels just above / in my 'bubbles' with my values in. Much like the '10' you can see on each bubble here.
What have I done to achieve this
This is not standard Chart.js functionality but I found this post which was discussing a similar issue for bar / line charts.
I've installed the plugin that post suggested but the data label it shows is for the radius of the bubble and I want to it to be the x-axis of the bubble.
I've also tried to use the code from some of the answers on that post, but with absolutely no luck.
My Code
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels"></script>
<div class="container" >
<h2>Chart.js — Line Chart Demo</h2>
<div>
<canvas id="myChart"></canvas>
</div>
</div>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
ctx.height = 1000;
var myChart = new Chart(ctx, {
type: 'bubble',
data: {
datasets: [
{
label: 'Your Data',
data: [
{x: 78.7, y: 0, r: 10, name: "Performance"}
],
backgroundColor: "rgba(153,255,51,0.6)"
},
{
label: 'Average',
data: [
{x: 100.7, y: 0, r: 10, name: "Performance"} // The labe needs to be X. not R.
],
backgroundColor: "rgba(255,0,128,0.6)"
}
]
},
options: {
maintainAspectRatio: false,
scales: {
yAxes: [{
id: 'first-y-axis',
type: 'linear',
ticks: {
min: 0,
max: 1,
stepSize: 1,
display: false
},
gridLines: {
display: false,
drawBorder: false
}
}],
xAxes: [{
ticks: {
min: 50, // Controls where axis starts
max: 120 // Controls where axis finishes
},
gridLines: {
display: false,
lineWidth: 3 // Width of bottom line
}
}]
}
}
});
</script>
Thanks in advance
I've managed to find the answer to this question, basically by taking apart the bubble chart example from the chartjs-plugin-datalabels plugin.
Below is a working example. Pay attention to the section in options that says 'plugin'.
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels"></script>
<div class="container" >
<h2>Chart.js — Line Chart Demo</h2>
<div>
<canvas id="myChart"></canvas>
</div>
</div>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
ctx.height = 1000;
var myChart = new Chart(ctx, {
type: 'bubble',
data: {
datasets: [
{
label: 'Your Data',
data: [
{x: 78.7, y: 0, r: 10, name: "Performance"}
],
backgroundColor: "rgba(153,255,51,0.6)"
},
{
label: 'Average',
data: [
{x: 100.7, y: 0, r: 10, name: "Performance"} // The labe needs to be
],
backgroundColor: "rgba(255,0,128,0.6)"
}
]
},
options: {
plugins: { // Look at this bit
datalabels: {
anchor: function(context) {
var value = context.dataset.data[context.dataIndex];
return value.x < 50 ? 'end' : 'center';
},
align: function(context) {
var value = context.dataset.data[context.dataIndex];
return value.x < 50 ? 'end' : 'center';
},
color: function(context) {
var value = context.dataset.data[context.dataIndex];
return value.x < 50 ? context.dataset.backgroundColor : 'white';
},
font: {
weight: 'bold'
},
formatter: function(value) {
return Math.round(value.x);
},
offset: 2,
padding: 0
}
},
maintainAspectRatio: false,
scales: {
yAxes: [{
id: 'first-y-axis',
type: 'linear',
ticks: {
min: 0,
max: 1,
stepSize: 1,
display: false
},
gridLines: {
display: false,
drawBorder: false
}
}],
xAxes: [{
ticks: {
min: 50, // Controls where axis starts
max: 120 // Controls where axis finishes
},
gridLines: {
display: false,
lineWidth: 3 // Width of bottom line
}
}]
}
}
});
</script>
If all you want to do is changing the label, there is an easier solution. From the docs of chartjs-plugin-datalabels:
Data values are converted to string ('' + value). If value is an object, the following rules apply first:
value = value.label if defined and not null
else value = value.r if defined and not null
else value = 'key[0]: value[key[0]], key[1]: value[key[1]], ...'
Therefore, it is sufficient to specify a label in your data points:
data: [{ x: 78.7, y: 0, r: 10, name: "Performance", label: `${Math.round(x)}` }],
C3 graph overlap x-axis labels which is in date time format. I have googled this query but didn't get any solution for it.Is it possible that c3 only shows couple of date time rather then showing altogether which result in overlap x-axis labels
var data = {
x: 'x',
xFormat:'%Y-%m-%d/%H:%M',
empty: {
label: {
text: "No Data"
}
},
columns: [
['x', '{$dateArray}'],
['Attack', {$data}],
],colors: {
Attack: '#67b7dc',
},
types: {
Attack: 'area'
}};
var chart = c3.generate({bindto: '#chart1',
size: {
height: 630,
},
data: data,
grid: {
x: {
show: true
},
y: {
show: true
}
},
tooltip: {
format: {
value: function (value, ratio, id) {
var format = value+' Gbps [ IP: '+destIp[value]+' ]';
return format;
}
}
},
zoom: {
enabled: true
},
subchart: {
show: true
},axis: {
x: {
type: 'timeseries',
tick: {
format: '%b %d, %H:%M',
rotate: 90,
multiline: false
}
},
y: {
tick: {
format: function (d) {
return d.toFixed(3);
}
},
label: {
text: 'Attack Size ( Gbps )',
position: 'outer-middle'
}
}
}
});
Use c3.js chart config tick.count, set it to desired integer value like 2,3 or 4.
Use c3.js Timeseries Chart exmaple to play with this config.
Problem without tick.count:-
Problem solved with tick.count config:-
I have a graph plotted with a multitude of lines, a rather big figure overlapping the lines and ontop of that is a point that is capable of being drag and dropped all over the plot by the user.
The problem I'm facing currently is that as soon as the user drags and drops the point straight ontop of a line or a point of the figure, the user is unable to drag and drop the point away. I have set up a fiddle with my current setup.
JavaScript/jQuery code:
$(function() {
var startPoint = [[7.00, 0]];
var line10 = HHIsoPleth(7.00, 7.80, 10);
var line120 = HHIsoPleth(7.00, 7.80, 120);
var options = {
series: {
points: {
editMode: "none",
show: true,
radius: 0,
symbol: "circle",
fill: true,
hoverable: false,
},
lines: {
editMode: "none",
editable: false,
hoverable: false,
clickable: false
}
},
yaxes: [ {
position: "left",
min: 0, max: 60,
tickSize: 4,
} ],
xaxes: [ {
position: "bottom",
min: 7.00, max: 7.80,
} ],
grid: {
backgroundColor: "transparent",
editable: true,
hoverable: true,
clickable: false,
},
legend: {
position: "nw"
},
};
var data = [
{ data: line10, label: "PCO2", lines: { show: true, lineWidth: 1 }, points: { show: false }, editable: false, clickable: false, hoverable: false, color: "#FF0000" },
{ data: line120, lines: { show: true, lineWidth: 1 }, points: { show: false }, editable: false, clickable: false, hoverable: false, color: "#FF0000" },
{ data: startPoint, label: "Bloedzuur gehalte", lines: { show: true }, points: { show: true, radius: 3 }, editable: true, editMode: 'xy', color: '#00FF00' },
];
var plot = $.plot($("#flot-placeholder"), data, options);
// Drag and drop
$("#flot-placeholder").bind("datadrop", function(event, pos, item) {
var PCO2 = getPCO2(pos.x1.toFixed(2), pos.y1.toFixed(2));
var pH = getPH(pos.y1.toFixed(2), PCO2);
var HCOmm = getHCO3(pH, PCO2);
updatePoint(pH, HCOmm);
});
// Generate red lines / isopleths
function HHIsoPleth(minPH, maxPH, PCO2){
var isoPleth = [];
for (var i = minPH; i < maxPH; i+=0.01){
HCOmm = (0.03 * PCO2 * Math.pow(10,i-6.1));
isoPleth.push([i,HCOmm]);
}
return isoPleth;
}
function getHCO3(ph, pco2) {
return 0.03 * pco2 * Math.pow(10, ph - 6.1);
}
function getPH(hco3, pco2) {
return 6.1 + Math.log10(hco3 / (0.03 * pco2));
}
function getPCO2(ph, hco3) {
return (hco3 / 0.03) * Math.pow(10, 6.1 - ph);
}
//Reset point
$("#davenportReset").click(function() {
updatePoint(7.00, 0);
});
function updatePoint(x, y) {
data[16].data[0] = [x, y];
$.plot($("#flot-placeholder"), data, options);
}
// Debug purpose, get the index of the point that is clicked
$("#placeholder").bind("plotdown", function(event,pos,item){
$("#log").append("\nplotdown(" + item.seriesIndex + ")");
});
});
Additional libraries: Flot.js, JUMFlot
HTML:
<input class="davenportInput" id="davenportReset" type="button" value="Reset Point" />
<div id="flot-placeholder" style="width:558px;height:511px"></div>
eventlog<textarea id="log" rows="15" cols="28"></textarea>
In the provided fiddle you'll see that you can drag and drop the green point all around the plot. But once you drop it ontop any of the red lines it is no longer possible to drag and drop the green point somewhere else. In the textarea you'll see that when you click the green point, plotdown(16) will be shown in the textarea. But will show plotdown(0-15) when it is clicked when the point is over any of the red/yellow lines.
Would it be possible to get the 16th data serie(the drag and drop point) when it's overlapping any of the red lines?
Using (once again) Mark's answer I solved it. One condition I had though was that I had to keep the green point above all other lines.
This is what I did:
var startPoint = [[7.00, 0]];
var invisPoint = [[7.00, 0]];
var line10 = HHIsoPleth(7.00, 7.80, 10);
var line120 = HHIsoPleth(7.00, 7.80, 120);
To create a invisible placeholder point.
I than added it to the data object
var data = [
{ data: invisPoint , lines: { show: false }, points: { show: false, radius: 3 }, editable: true, editMode: 'xy', color: '#00FF00' },
{ data: line10, label: "PCO2", lines: { show: true, lineWidth: 1 }, points: { show: false }, editable: false, clickable: false, hoverable: false, color: "#FF0000" },
{ data: line120, lines: { show: true, lineWidth: 1 }, points: { show: false }, editable: false, clickable: false, hoverable: false, color: "#FF0000" },
{ data: startPoint, label: "Bloedzuur gehalte", lines: { show: true }, points: { show: true, radius: 3 }, editable: true, editMode: 'xy', color: '#00FF00' },
];
And updated the updatePoint function
function updatePoint(x, y) {
var data = plot.getData();
data[0].data[0] = [x, y]; // Invisible point
data[17].data[0] = [x, y]; // Green point
plot.setData(data);
plot.draw();
}
This way, the invisible point gets selected and dragged and dropped. I simply use those coordinates to position the green point aswell.
Internally, flot or jumflot in this case, when you mousedown is searching the points to see if one is near enough to your mouse cursor. It searches the points in order and finds your line segment before the point. So, simple fix, place your move-able point first:
var data = [
{ data: startPoint, label: "Bloedzuur gehalte", lines: { show: true }, points: { show: true, radius: 3 }, editable: true, editMode: 'xy', color: '#00FF00' },
{ data: line10, label: "PCO2", lines: { show: true, lineWidth: 1 }, points: { show: false }, editable: false, clickable: false, hoverable: false, color: "#FF0000" },
....
In addition, update your plot like this:
function updatePoint(x, y) {
var data = plot.getData();
data[0].data[0] = [x, y];
plot.setData(data);
plot.draw();
}
Calling $.plot over and over again is expensive and will probably leak memory (it used to at least - not sure if it was every fixed).
Updated fiddle.