Related
I have two different graphs that I need to make. A simple bar graph, one with and one without labels on the xaxis. The design calls for this to be equal width, and no padding/margin on either side. As in it needs to line up with other objects.
The one chart uses the
sparkline: {
enabled: true
},
option. The one with the labels gets the wrong width because of the labels. I have tried with css, but using magic numbers isn't smart because the values of the labels can change based on values, and thus the width.
Code for both:
https://codepen.io/cibgraphics/pen/JjZaxGB
Image of problem
I think I have just found a quite experimental solution to solve this problem. It is not very clean, but it is interesting to share...
The idea is to play with the viewBox attribute of your second svg element. It is documented here: viewBox - SVG: Scalable Vector Graphics | MDN
I also use the SVGGraphicsElement.getBBox() method.
The SVGGraphicsElement.getBBox() method allows us to determine the coordinates of the smallest rectangle in which the object fits.
SVGGraphicsElement.getBBox() - Web APIs | MDN
When the page loads, we can set the viewBox using the coordinates returned by SVGGraphicsElement.getBBox(). It works, but if you resize the window, another svg element is created and replace the current one. Thus, the viewBox is lost.
So the idea is now to use a MutationObserver to watch for changes in the DOM. Here we observe #barChart2 because we are interested in one of its children (which is the svg element). When a change is detected, the custom noPadding() function is called.
Here is the code:
function noPadding() {
let svg = document.querySelector('#barChart2 svg'),
{x, y, width, height} = svg.getBBox();
svg.setAttribute('viewBox', `${x} ${y} ${width} ${height}`);
}
let node = document.querySelector('#barChart2'),
config = { childList: true },
observer = new MutationObserver(() => noPadding());
observer.observe(node, config);
noPadding();
If I put the above code in what you did, we get this:
var barChartOptions = {
series: [{
name: 'Actual',
data: [{
x: '2011',
y: 12,
},
]
}],
chart: {
type: 'bar',
height: '22px',
sparkline: {
enabled: true
},
},
plotOptions: {
bar: {
horizontal: true,
borderRadius: 10,
barHeight: '100%',
colors: {
backgroundBarColors: ['#E7E7E6'],
backgroundBarRadius: '11px',
}
},
},
colors: ['#3CDBC0'],
tooltip: {
custom: function({ series, seriesIndex, dataPointIndex, w }) {
return (
'<div class="arrow_box">' +
"<span>" +
series[seriesIndex][dataPointIndex] +
"</span>" +
"</div>"
);
}
}
};
var chart = new ApexCharts(document.querySelector("#barChart1"), barChartOptions);
chart.render();
var barChartOptions2 = {
series: [{
name: 'Actual',
data: [{
x: '2011',
y: 10,
},
]
}],
chart: {
type: 'bar',
height: '90px',
toolbar: {
show: false
},
},
grid: {
show: false,
padding: {
right: 0,
left: 0
}
},
yaxis: {
show: false,
},
xaxis: {
axisTicks: {
color: '#000',
},
},
dataLabels: {
enabled: false,
},
plotOptions: {
bar: {
horizontal: true,
borderRadius: 10,
columnWidth: '100%',
barHeight: '100%',
colors: {
backgroundBarColors: ['#E7E7E6'],
backgroundBarRadius: '11px',
},
},
},
colors: ['#3CDBC0'],
legend: {
show: false,
},
tooltip: {
custom: function({ series, seriesIndex, dataPointIndex, w }) {
return (
'<div class="arrow_box">' +
"<span>" +
series[seriesIndex][dataPointIndex] +
"</span>" +
"</div>"
);
}
}
};
var chart = new ApexCharts(document.querySelector("#barChart2"), barChartOptions2);
chart.render();
// ==================================================
function noPadding() {
let svg = document.querySelector('#barChart2 svg'),
{x, y, width, height} = svg.getBBox();
svg.setAttribute('viewBox', `${x} ${y} ${width} ${height}`);
}
let node = document.querySelector('#barChart2'),
config = { childList: true },
observer = new MutationObserver(() => noPadding());
observer.observe(node, config);
noPadding();
.barChart {
margin-bottom: 50px;
}
.apexcharts-tooltip {
filter: drop-shadow(0px 3.15331px 15.778px rgba(0, 45, 93, 0.2));
}
.arrow_box {
position: relative;
background: #fff;
}
.arrow_box:after, .arrow_box:before {
right: 100%;
top: 50%;
transform: translate(0, -50%);
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.arrow_box:after {
border-color: rgba(85, 85, 85, 0);
border-right-color: #fff;
border-width: 10px;
// margin-top: -10px;
transform: translate(0, -50%);
}
.arrow_box:before {
border-color: rgba(0, 0, 0, 0);
border-right-color: #fff;
border-width: 13px;
// margin-top: -13px;
transform: translate(0, -50%);
}
.barChart .apexcharts-tooltip {
color: #000;
transform: translateX(10px) translateY(10px);
overflow: visible !important;
white-space: normal !important;
}
.barChart .apexcharts-tooltip span {
padding: 5px 10px;
display: inline-block;
}
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<div id="barChart1" class="barChart"></div>
<div id="barChart2" class="barChart"></div>
I am creating 5 sections of gauge using chartjs-gauge. I am using the following data.
[150,200,250,300,400]
From this data, I want to display the circumference until 300. But the angle should calculated by including the last section value too. I had custom the text showing in section by setting it to empty string if more than 300. For section colour, I set 4 colours["green", "yellow", "orange", "red"]. Now, last section showing as silver colour which is default background of gauge. I have add rgba(0,0,0,0) to colour array ["green", "yellow", "orange", "red","rgba(0,0,0,0)"] which will show transparent colour for last section. But, when hover on section, it is responsive showing border. I would like to know if have other way to show the circumference until certain value from our data ,but calculating section area in chart using all value from data.
var data = [150, 200, 250, 300, 400];
var config = {
type: "gauge",
data: {
labels: ['Success', 'Warning', 'Warning', 'Error'],
datasets: [{
data: data,
value: 300,
backgroundColor: ["green", "yellow", "orange", "red"],
borderWidth: 2
}]
},
options: {
responsive: true,
title: {
display: true,
text: "Gauge chart with datalabels plugin"
},
layout: {
padding: {
bottom: 30
}
},
needle: {
// Needle circle radius as the percentage of the chart area width
radiusPercentage: 2,
// Needle width as the percentage of the chart area width
widthPercentage: 3.2,
// Needle length as the percentage of the interval between inner radius (0%) and outer radius (100%) of the arc
lengthPercentage: 80,
// The color of the needle
color: "rgba(0, 0, 0, 1)"
},
valueLabel: {
formatter: Math.round
},
plugins: {
datalabels: {
display: true,
formatter: function(value, context) {
//return '>'+value;
if (value <= 300) {
return value;
} else {
return '';
}
},
color: function(context) {
//return context.dataset.backgroundColor;
return 'black';
},
//color: 'rgba(255, 255, 255, 1.0)',
/*backgroundColor: "rgba(0, 0, 0, 1.0)",*/
borderWidth: 0,
borderRadius: 5,
font: {
weight: "bold"
}
}
}
}
};
window.onload = function() {
var ctx = document.getElementById("chart").getContext("2d");
window.myGauge = new Chart(ctx, config);
};
canvas {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en-US">
<head>
<script src="jQuery/jquery-3.4.1.min.js"></script>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Gauge Chart with datalabels plugin</title>
<script src="https://unpkg.com/chart.js#2.8.0/dist/Chart.bundle.js"></script>
<script src="https://unpkg.com/chartjs-gauge#0.3.0/dist/chartjs-gauge.js"></script>
<script src="https://unpkg.com/chartjs-plugin-datalabels#0.7.0/dist/chartjs-plugin-datalabels.js"></script>
</head>
<body>
<div id="canvas-holder" style="width:100%">
<canvas id="chart"></canvas>
</div>
</body>
</html>
var data = [150, 200, 250, 300, 400];
colour_array = ["#11d8ee", "#3cc457", "#f12b0e", "#dda522", "#808080"];
let sum = data.reduce(function(a, b) {
return a + b;
}, 0);
var perc = 0;
perc_array = [];
for (i = 0; i < data.length; i++) {
perc = (data[i] / sum * 100).toFixed(2);
perc_array.push(perc);
}
Chart.plugins.register({ //increase distance between legend and chart
id: 'paddingBelowLegends',
beforeInit: function(chart, options) {
chart.legend.afterFit = function() {
this.height = this.height + 50; //custom 50 to value you wish
};
}
});
//when want to disable this plugin in other chart, paddingBelowLegends: false in plugin{}
var config = {
type: "doughnut",
data: {
labels: ['A', 'B', 'C', 'D', 'Others'],
datasets: [{
data: data,
value: data[(colour_array.length - 1)], //300
backgroundColor: colour_array,
borderWidth: 2
}]
},
options: {
responsive: true,
cutoutPercentage: 60,//thickness of chart
title: {
display: true,
text: "Gauge chart with datalabels plugin"
},
layout: {
padding: {
bottom: 30
}
},
valueLabel: {
formatter: Math.round,
display: false // hide the label in center of gauge
},
plugins: {
beforeInit: function(chart, options) {
chart.legend.afterFit = function() {
this.height = this.height + 50;
};
},
outlabels: {
display: true,
//text: '%l %v %p',//(label value percentage)the percentage automatically roundoff
//hide chart text label for last section-https://github.com/Neckster/chartjs-plugin-piechart-outlabels/issues/10#issuecomment-716606369
text: function(label) {
console.log(label);
highest_index = label['labels'].length - 1; //get highest index from the labels array
current_index = label['dataIndex']; //current index
value = label['dataset']['data'][label['dataIndex']]; //value of current index
const v = parseFloat(label['percent']) * 100;
if (current_index != highest_index) //to hide last section text label on chart.
{
//return value + ' , ' + `${v.toFixed(2)}%`;
return value+',\n'+`${v.toFixed(2)}%`;
} else {
return false;
}
},
color: 'white',
stretch: 12, //length of stretching
font: {
resizable: true,
minSize: 10,
maxSize: 14
},
padding: {
/*left:25,
right: 0
top:0,
bottom:0*/
}
},
//inner label:
datalabels: { //label on arc section
display: false,
formatter: function(value, context) {
if (value <= data[(colour_array.length - 2)]) //hide datalabel for last section
{
id = data.indexOf(value);
perc = perc_array[id];
return value + ' , ' + perc + '%';
} else {
return '';
}
},
color: function(context) {
return 'black';
},
borderWidth: 0,
borderRadius: 10,
font: {
weight: "bold",
},
anchor: "end" //'center' (default): element center, 'start': lowest element boundary, 'end': highest element boundary
}
},
legend: { //filter last section from legend chart labels
display: true,
//position: 'right',
labels: {
filter: function(legendItem, data) {
//ori-return legendItem !=1;
return !legendItem.text.includes('Others');
},
boxWidth: 20
}
},
rotation: 1 * Math.PI,
circumference: 1 * Math.PI,
tooltips: {
enabled: true,
mode: 'single',
filter: function(tooltipItem, data) { //disable display tooltip in last section
var label = data.labels[tooltipItem.index];
if (label == "Others") {
return false;
} else {
return true;
}
},
callbacks: { //custom tooltip text to show percentage amount (by default,showing real amount)
label: function(tooltipItem, data) {
var dataset = data.datasets[tooltipItem.datasetIndex];
hovered_index = tooltipItem.index;
data_length = data.datasets[0].data.length;
var total = dataset.data.reduce(function(previousValue, currentValue, currentIndex, array) {
return previousValue + currentValue;
});
var currentValue = dataset.data[tooltipItem.index];
var percentage = (currentValue / total * 100).toFixed(2);
return currentValue + ' , ' + percentage + "%";
}
}
}
}
};
window.onload = function() {
var ctx = document.getElementById("chartJSContainer").getContext("2d");
window.myGauge = new Chart(ctx, config);
};
<html>
<head>
<script src="https://unpkg.com/chart.js#2.8.0/dist/Chart.bundle.js"></script>
<script src="https://unpkg.com/chartjs-gauge#0.3.0/dist/chartjs-gauge.js"></script>
<script src="https://unpkg.com/chartjs-plugin-datalabels#0.7.0/dist/chartjs-plugin-datalabels.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-piechart-outlabels"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div id="canvas-holder" style="width:50% align:center">
<canvas id="chartJSContainer"></canvas>
</div>
</body>
</html>
I am working on a horizontal bar graph and the y-labels are a bit long(they take up half the space in mobile view).
I want to know if it is possible to shift the labels inside the bars.
This is what I have
var chart = c3.generate({
bindto: '#IndivisualScore',
bar: {
width: 15,
space: 2
},
padding: {
left: 100
},
color: {
pattern: ['#FABF62', '#ACB6DD','#FABF62', '#ACB6DD','#FABF62', '#ACB6DD','#FABF62', '#ACB6DD','#FABF62', '#ACB6DD','#FABF62', '#ACB6DD','#FABF62' ]
},
data: {
x: 'x',
columns:
[
['x', "Initiative", "Sees and Acts on opportunities", "Persistence", "Information seeking", "Concern for high Quality of work","Commitment to Work Contract","Efficiency Orientation","Systematic Planning","Problem Solving","Self-confidence","Assertiveness","Persuasion","Use of influence Strategies"],
['value', 300, 400,245,342,532,213,452,344,123,533,234,231,324]
],
type: 'bar',
color: function(inColor, data) {
var colors = ['#FABF62', '#ACB6DD','#FABF62', '#ACB6DD','#FABF62', '#ACB6DD','#FABF62', '#ACB6DD','#FABF62', '#ACB6DD','#FABF62', '#ACB6DD','#FABF62' ];
if(data.index !== undefined) {
return colors[data.index];
}
return inColor;
},
labels: true
},
axis: {
rotated: true,
x: {
type: 'category'
}
},
tooltip: {
grouped: true
},
legend: {
show: true
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.9/c3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="IndivisualScore"></div>
Basic solution is to apply transform to all labels:
.c3-text {
transform: translateX(-25px);
fill: #000 !important; /* to make them visible */
}
See this fiddle.
More advanced solution could be to measure width of every label and set translateX values individually.
I have created a chart using C3.JS. It has bar graph with line graph like as shown below
The graph is working fine but I have a requirement that I need a line graph to be shown within the tooltip along with other data points.
The line graph is coming but the other data points are missing (x, data1, data2). In addition tooltip should come only on mouse click not mouse over also by default when the page loads the tooltip should appear for the last bar.
Current my tooltip shows only with the line graph like as shown below without the other data points(x, data1, data2)
But I expect my tooltip to appear like as shown below
My code is as given below
Can anyone please help me on this
Working JSFiddle
function generateGraph(tooltip, data1, data2) {
// if the data is same as before don't regenrate the graph - this avoids flicker
if (tooltip.data1 &&
(tooltip.data1.name === data1.name) && (tooltip.data1.value === data1.value) &&
(tooltip.data2.name === data2.name) && (tooltip.data2.value === data2.value))
return;
tooltip.data1 = data1;
tooltip.data2 = data2;
// remove the existing chart
if (tooltip.chart) {
tooltip.chart = tooltip.chart.destroy();
tooltip.selectAll('*').remove();
}
// create new chart
tooltip.chart = c3.generate({
bindto: tooltip,
padding: {
right: 15
},
size: {
width: 200,
height: 200
},
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250, 160],
['data2', 50, 20, 10, 40, 15, 25, 34]
]
},
tooltip: {
show: false
}
});
// creating a chart on an element sets its position attribute to relative
// reset it to absolute (the tooltip was absolute originally) for proper positioning
tooltip.style('position', 'absolute');
tooltip.style('background-color', 'white');
}
var chart = c3.generate({
data: {
columns: [
['x', 1000, 200, 150, 300, 200],
['data1', 1000, 200, 150, 300, 200],
['data2', 400, 500, 250, 700, 300], ],
axes: {
'data1': 'y2'
},
type: 'bar',
types: {
'data1': 'line'
}
},
tooltip: {
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
// this creates a chart inside the tooltips
var content = generateGraph(this.tooltip, d[0], d[1])
// we don't return anything - see .html function below
}
}
});
// MONKEY PATCHING (MAY break if library updates change the code that sets tooltip content)
// we override the html function for the tooltip to not do anything (since we've already created the tooltip content inside it)
chart.internal.tooltip.html = function () {
// this needs to return the tooltip - it's used for positioning the tooltip
return chart.internal.tooltip;
}
Live Demo:
http://jsfiddle.net/blackmiaool/y7Lhej4m/
Instead of overriding the .html function of tooltip, I use contents and css to customize the tooltip.
js:
contents: function (d, defaultTitleFormat, defaultValueFormat, color) {
generateGraph(this.tooltip, d[0], d[1]);
var tip = this.tooltip[0][0].querySelector(".c3-tooltip");//find previous tooltip
if (tip) {
tip.parentElement.removeChild(tip);//remove it
}
return this.getTooltipContent.apply(this, arguments) + this.tooltip.html();//concat default tooltip and customized one
}
css:
.c3-tooltip-container {
max-height: none !important;
}
.c3-tooltip {
box-shadow: none;
width: 100%;
}
.c3-tooltip tr {
border-left: none !important;
border-right: none !important;
}
.c3-tooltip td.name {
border-left: none !important;
}
.c3-tooltip tr:first-child {
display: none;
}
.c3-tooltip-container .c3-chart-line {
opacity: 1 !important;
}
I have a structure of chart like this:-
data: {
cols: [
{
id: 'Type',
type: 'string'
},
{
id: 'percentage',
type: 'number'
},
{
id: 'tooltip',
role: 'tooltip',
type: 'string',
p: { html: true }
}
],
rows: [
{
c: [
{
v: typeA
},
{
v: 20
},
{
v: 'my Tooltip content'
}
]
},
{
c: [
{
v: 'typeB'
},
{
v: 80
}
]
}
]
},
I want to disable the tooptip only for typeB and but should work with typeA. Is this possible in google charts? (tooptip trigger:none option disable it for whole chart)
when using custom tooltips, if the tooltip column is null or ''
the chart will replace with the default tooltip
to avoid, provide a custom tooltip that is hidden with css
see following working snippet...
google.charts.load('current', {
callback: function () {
var data = google.visualization.arrayToDataTable([
['Type', 'Percent'],
['typeA', 20],
['typeB', 80]
]);
// add tooltip column
data.addColumn({type: 'string', role: 'tooltip', p: {html: true}});
// build tooltip
for (var i = 0; i < data.getNumberOfRows(); i++) {
switch (data.getValue(i, 0)) {
// set visible tooltip
case 'typeA':
data.setValue(i, 2,
'<div class="ggl-tooltip"><div><span>' +
data.getValue(i, 0) + '</span></div><div>' +
data.getValue(i, 1) + '</div></div>'
);
break;
// set hidden tooltip
case 'typeB':
data.setValue(i, 2, '<div class="hdn-tooltip"><div>');
break;
}
}
var container = document.getElementById('chart_div');
var pieChart = new google.visualization.PieChart(container);
pieChart.draw(data, {
tooltip: {
isHtml: true
}
});
},
packages: ['corechart']
});
.hdn-tooltip {
display: none;
visibility: hidden;
}
.ggl-tooltip {
border: 1px solid #E0E0E0;
font-family: Arial, Helvetica;
font-size: 10pt;
padding: 12px 12px 12px 12px;
}
.ggl-tooltip div {
padding-top: 6px;
}
.ggl-tooltip span {
font-weight: bold;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>