Draw points and lines inside a bar in bar chart - charts

Who know how to draw lines and points inside a bar something like this:

You can draw the lines as floating bars where individual bars are specified with the syntax [min, max], same as you already do for your your other bars. Given an array of number values, the "line" data can be produced with Array.map() as follows.
data: [110, 150, 140, 100, 120].map(v => [v - 1, v + 1])
Then you need to define individual stacked xAxes for the "bar" and the "line" datasets. In order to have the original "line" values displayed in the tooltips, a toolips.callback.label function is also needed.
The points can be defined in an additional dataset of type 'scatter'.
Please have a look at below runnable code snippet.
new Chart("chart", {
type: "bar",
data: {
labels: ["4", "3", "2", "1", "0"],
datasets: [{
label: "Bars",
backgroundColor: "rgba(0, 255, 0, 0.2)",
data: [[20, 100], [50, 180], [60, 120], [10, 130], [70, 140]],
xAxisID: "x-axis-actual",
order: 1
},
{
label: "Lines",
backgroundColor: "rgb(0, 0, 0)",
data: [90, 150, 110, 90, 120].map(v => [v - 1, v + 1]),
xAxisID: "x-axis-target",
order: 2
},
{
label: "Points",
backgroundColor: "rgb(255, 255, 255)",
borderColor: "rgb(0, 0, 0)",
data: [80, 110, 90, 100, 120],
type: "scatter"
}
]
},
options: {
legend: {
display: false
},
tooltips: {
callbacks: {
label: (tooltipItem, data) => {
const dataset = data.datasets[tooltipItem.datasetIndex];
const v = dataset.data[tooltipItem.index];
return dataset.label + ': ' + (tooltipItem.datasetIndex == 1 ? (v[1] + v[0]) / 2 : tooltipItem.value);
}
}
},
scales: {
xAxes: [{
id: "x-axis-target",
stacked: true
},
{
display: false,
offset: true,
stacked: true,
id: "x-axis-actual",
gridLines: {
offsetGridLines: true
}
}
],
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart" height="120"></canvas>

Related

(Chart.js 3.7.0) change position of x-axis ticks to alternate between each tick?

I have a chart which effectively renders a timeline, but sometimes the tick positions are too close together
Current graph (padding: 25)
I can change the padding to be negative which puts them above the x-axis:
Ticks above x-axis (padding: -60)
But I'd prefer them to alternate to above and below eg 1st tick below, 2nd tick above, 3rd tick below etc.
Can I access the individual ticks padding to do this? See below my current code:
ticks: {
source: 'data',
maxRotation: 90,
minRotation: 90,
font: {
size: 12,
},
autoskip: true,
padding: -60,
},
TIA for any help! :-)
You can define two identical datasets together with two x-axes. One of the x-axes with position: 'top', the other one with default position ('bottom').
A slightly different ticks.callback function on both x-axes makes sure that only every second tick is displayed.
ticks: {
source: 'data',
...
callback: (v, i) => i % 2 ? v : ''
},
Please take a look at the following runnable code and see how it works.
const data = [
{ x: "2022-03-22", y: 0 },
{ x: "2022-04-01", y: 0 },
{ x: "2022-04-02", y: 0 },
{ x: "2022-04-03", y: 0 },
{ x: "2022-04-08", y: 0 },
{ x: "2022-04-12", y: 0 },
{ x: "2022-04-15", y: 0 }
];
new Chart('chart', {
type: 'line',
data: {
datasets: [{
data: data,
backgroundColor: 'black',
pointRadius: 5,
pointHoverRadius: 5,
borderWidth: 2,
xAxisID: 'x'
},
{
data: data,
backgroundColor: 'black',
pointRadius: 5,
pointHoverRadius: 5,
borderWidth: 2,
xAxisID: 'x2'
}]
},
options: {
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label: context => undefined
}
}
},
scales: {
y: {
ticks: {
display: false,
},
grid: {
display: false,
drawBorder: false
}
},
x: {
position: 'top',
type: 'time',
time: {
unit: 'day',
tooltipFormat: 'MMM DD'
},
ticks: {
source: 'data',
minRotation: 90,
callback: (v, i) => i % 2 ? v : ''
},
grid: {
display:false,
drawBorder: false
}
},
x2: {
type: 'time',
time: {
unit: 'day',
tooltipFormat: 'MMM DD'
},
ticks: {
source: 'data',
minRotation: 90,
callback: (v, i) => i % 2 ? '' : v
},
grid: {
display:false,
drawBorder: false
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment-with-locales.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment#1.0.0"></script>
<canvas id="chart" height="50"></canvas>

Using ChartJS to create a multiple grouped bar chart - see picture below

I am testing out HighCharts and ChartJS to see which to use. For Highcharts, I was able to find a hack to create a bar chart that had double grouping on the x-axis.
This is what I want to look like:
I am new to both charting JS options and wonder if there is a way to do this in ChartJS.
I have the datasets something like this:
xAxis: {
categories: [{
name: "Total",
categories: ["2004", "2008", "2012"]
}, {
name: "Lower than 2.50",
categories: ["2004", "2008", "2012"]
}]
},
yAxis: {
min: 0,
title: {
text: 'Percent (%)'
}
},
series: [{
name: 'Male',
data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4]
}, {
name: 'Female',
data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6]
}]
Essentially I need a nest series on the x-axis and I am open to plugins or code from Github to do this.
You can make use of the Plugin Core API. It offers different hooks that may be used for executing custom code. In below code, I use the afterDraw hook to draw the category labels and the delimiter lines.
new Chart('myChart', {
type: 'bar',
plugins: [{
afterDraw: chart => {
let ctx = chart.chart.ctx;
ctx.save();
let xAxis = chart.scales['x-axis-0'];
let xCenter = (xAxis.left + xAxis.right) / 2;
let yBottom = chart.scales['y-axis-0'].bottom;
ctx.textAlign = 'center';
ctx.font = '12px Arial';
ctx.fillText(chart.data.categories[0], (xAxis.left + xCenter) / 2, yBottom + 40);
ctx.fillText(chart.data.categories[1], (xCenter + xAxis.right) / 2, yBottom + 40);
ctx.strokeStyle = 'lightgray';
[xAxis.left, xCenter, xAxis.right].forEach(x => {
ctx.beginPath();
ctx.moveTo(x, yBottom);
ctx.lineTo(x, yBottom + 40);
ctx.stroke();
});
ctx.restore();
}
}],
data: {
labels: ['2004', '2008', '2012', '2016', '2004', '2008', '2012', '2016'],
categories: ['Total', 'Lower than 2.50'],
datasets: [{
label: 'Male',
data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4, 51.2, 51.8],
backgroundColor: 'rgba(124, 181, 236, 0.9)',
borderColor: 'rgb(124, 181, 236)',
borderWidth: 1
},
{
label: 'Female',
data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6, 53.7, 54.6],
backgroundColor: 'rgba(67, 67, 72, 0.9)',
borderColor: 'rgb(67, 67, 72)',
borderWidth: 1
}
]
},
options: {
legend: {
position: 'bottom',
labels: {
padding: 30,
usePointStyle: true
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
max: 80,
stepSize: 20
},
scaleLabel: {
display: true,
labelString: 'Percent (%)'
}
}],
xAxes: [{
gridLines: {
drawOnChartArea: false
}
}]
}
}
});
canvas {
max-width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>
You have to define a second x-axis and play around with the many ticks and gridLines options.
Please take a look at below runnable code and see how it could be done. This is obviously only a draft and needs to optimized and made more generic.
new Chart(document.getElementById('myChart'), {
type: 'bar',
data: {
labels: ['2004', '2008', '2012', '2004', '2008', '2012'],
datasets: [{
label: 'Male',
data: [42.4, 43.0, 43.0, 50.3, 49.4, 48.4],
backgroundColor: 'rgba(124, 181, 236, 0.9)',
borderColor: 'rgb(124, 181, 236)',
borderWidth: 1
},
{
label: 'Female',
data: [57.6, 57.0, 57.0, 49.7, 50.6, 51.6],
backgroundColor: 'rgba(67, 67, 72, 0.9)',
borderColor: 'rgb(67, 67, 72)',
borderWidth: 1
}
]
},
options: {
legend: {
position: 'bottom',
labels: {
usePointStyle: true
}
},
scales: {
yAxes: [{
ticks: {
min: 0,
max: 80,
stepSize: 20
},
scaleLabel: {
display: true,
labelString: 'Percent (%)'
}
}],
xAxes: [{
gridLines: {
drawOnChartArea: false
}
},
{
offset: true,
ticks: {
autoSkip: false,
maxRotation: 0,
padding: -15,
callback: (v, i) => {
if (i == 1) {
return 'Total';
} else if (i == 4) {
return 'Lower than 2.50';
} else {
return '';
}
}
},
gridLines: {
drawOnChartArea: false,
offsetGridLines: true,
tickMarkLength: 20,
color: ['white', 'white', 'white', 'lightgray', 'white', 'white', 'lightgray']
}
}
]
}
}
});
canvas {
max-width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>

Echarts:how to set markLine lable above the line and at the end of the line?

In echarts-3.6.2, when I set position:'end' for markLine, the lable will display at the end of line
markLine: {
data: [{
symbol:"none",
name: 'GOAL',
yAxis: 3.12 ,
label:{
normal:{
show:true,
position:'end'
}
},
lineStyle: {
normal: {
color: '#5C57FF',
width: 2
}
},
}]
},
However, I want to dislay it above the line at the end of the line? How to make it?
Change position value to insideEndTop(see in docs):
markLine: {
data: [{
symbol: "none",
name: 'GOAL',
yAxis: 3.12,
label: {
normal: {
show: true,
position: 'insideEndTop'
}
},
lineStyle: {
normal: {
color: '#5C57FF',
width: 2
}
},
}]
},
hello,do you have any ideas for not using position: 'insideEndTop', I could not upgrade the echarts plugin
I can't help without a crutch/workaround because it's very old version. You need to update the Echarts immediately, it's only right way. Or you can try to simulate markLine with the graphic component, something like below but it's highway to hell.
var myChart = echarts.init(document.getElementById('main'));
var option = {
color: ['rgba(92, 87, 255, 0.3)'],
grid: {
left: 50,
bottom: 50,
},
graphic: [{
type: 'group',
id: 'markLine',
bounding: 'raw',
children: [{
id: 'lineShape',
$action: 'replace',
type: 'line',
invisible: true,
shape: {
x1: 50,
y1: 300,
x2: 120,
y2: 300,
},
style: {
stroke: '#5C57FF',
lineWidth: 2,
},
zlevel: 10,
}, {
type: 'polygon',
$action: 'replace',
id: 'arrowShape',
invisible: true,
scale: [0.5, 0.3],
position: [90, 292.5],
shape: {
points: [
[16, 5],
[16, 47],
[38, 26]
]
},
style: {
fill: '#5C57FF',
}
}, {
type: 'text',
$action: 'replace',
id: 'labelShape',
invisible: true,
style: {
text: 'GOAL: 3.12',
x: -100,
y: 290,
fill: '#5C57FF',
font: 'bolder 12px sans-serif',
},
zlevel: 10,
}],
}],
xAxis: {
data: ["1", "2", "3", "4", "5", "6"]
},
yAxis: {
type: 'value',
max: 50
},
series: [{
name: 'Series',
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
}]
};
myChart.setOption(option);
function renderMarkLine({ instance, yAxisValue, text, speed }){
var currentStep = 0;
var arrowShape = (val) => {
return {
stopCoord: 710, // 525
opts: {
invisible: false,
id: 'arrowShape',
position: [5 + val, yAxisValue - 7.5] // yAxisValue + 7.5
}
}
};
var lineShape = (val) => {
return {
stopCoord: 680, //540
opts: {
id: 'lineShape',
invisible: false,
shape: {
x1: 50,
y1: yAxisValue, // +0
x2: 50 + val,
y2: yAxisValue
}
}
}
};
var labelShape = (val) => {
return {
stopCoord: 660, // 460
opts: {
id: 'labelShape',
invisible: false,
style: {
x: -10 + val,
y: yAxisValue - 10, // 10
fill: '#5C57FF',
font: 'bolder 12px sans-serif'
}
}
}
};
var interval = setInterval(function(){
var graphicData = [];
[arrowShape, lineShape, labelShape].forEach(el => {
if (el(null).stopCoord > currentStep){
graphicData.push(el(currentStep).opts);
}
});
if (graphicData.length === 0) clearInterval(interval);
instance.setOption({ graphic: graphicData });
currentStep += 10;
}, speed);
};
renderMarkLine({ instance: myChart, yAxisValue: 500, speed: 0 });
<script src="https://cdn.jsdelivr.net/npm/echarts#3.6.2/dist/echarts.min.js"></script>
<div id="main" style="width:800px;height:600px;"></div>

echarts - visualMap according to y axis

I am trying to add a visual map according to the y axis in echarts.
Taking one of their example:
https://echarts.apache.org/examples/en/editor.html?c=area-pieces
The results looks as follow:
what I'm trying to achieve is:
Obviously here, I have just rotated the picture by 90 degree.
How can this be achieve in echarts directly (so not by saving the picture first)?
The simplest solution would be inverting the axis, data index, and visual map axis. See chart options below:
option = {
backgroundColor: '#fff',
xAxis: {
type: 'value',
boundaryGap: [0, '30%'],
position: 'top'
},
yAxis: {
type: 'category',
boundaryGap: false
},
visualMap: {
type: 'piecewise',
show: false,
dimension: 1,
seriesIndex: 0,
pieces: [{
gt: 1,
lt: 3,
color: 'rgba(0, 180, 0, 0.5)'
}, {
gt: 5,
lt: 7,
color: 'rgba(0, 180, 0, 0.5)'
}]
},
series: [
{
type: 'line',
smooth: 0.6,
symbol: 'none',
lineStyle: {
color: 'green',
width: 5
},
markLine: {
symbol: ['none', 'none'],
label: {show: false},
data: [
{yAxis: 1},
{yAxis: 3},
{yAxis: 5},
{yAxis: 7}
]
},
areaStyle: {},
data: [
[200, '2019-10-10'],
[400, '2019-10-11'],
[650, '2019-10-12'],
[500, '2019-10-13'],
[250, '2019-10-14'],
[300, '2019-10-15'],
[450, '2019-10-16'],
[300, '2019-10-17'],
[100, '2019-10-18']
]
}
]
};
Result:
See on Imgur

ECharts - Getting a single splitLine to render in a stacked bar chart

Trying to get a single split line to render on a stacked bar graph.
Here's the code:
options = {
tooltip: {
trigger: 'item',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['Graph 1', 'Graph 2']
},
grid: {
left: '7%',
right: '5%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: [40, 41, 42, 43, 44],
axisLabel: {
interval: 1
},
splitLine: {
show: true,
interval: function (param) {
//return param === 1; //what I'm trying to get it to do
return param > 0 && param < 2; //this doesn't work
//return param > 0; //this works, but adds a split line to everything above 1 as well, not what I want. vice versa (param < 2) also works, but again not what I want
},
lineStyle: {
type: 'dashed',
width: 2,
color: '#767676'
}
}
}
],
yAxis: [
{
type: 'value',
name: 'Y',
nameLocation: 'middle',
nameTextStyle: {
padding: [0, 0, 8, 0],
color: '#767676',
fontSize: 14
},
axisTick: {
show: false
},
axisLabel: {
show: false
},
splitLine: {
show: false
}
}
],
series: [
{
name: 'Graph 1',
type: 'bar',
barWidth: 20,
stack: 'Graph',
itemStyle: {
color: '#db0011'
},
data: [8000, 10000, 12000, 16000, 20000]
},
{
name: 'Graph 2',
type: 'bar',
barWidth: 20,
barGap: '-100%',
stack: 'Graph',
itemStyle: {
color: '#00a69d'
},
data: [4000, 5000, 6000, 8000, 10000]
}
]
};
As per the above code,
param > 0 works but will add a split line to everything beyond 1 (1 and 2)
likewise, param < 2 will add a split line to everything before 2 (0 and 1)
param > 0 and param < 2 doesn't work (no split line appears)
setting a fixed number causes a split line to appear at the end of the graph as well, despite not being in the right interval (e.g. if my items went from 40 - 80 and I set an interval of 7, then the split line would appear before 47, 54, 61, 68, 75 and on 40 and 80)
How do I get a single split line to appear?
I've also seen Echarts how do you add dashed vertical line in between specific bars on bar chart?, however I don't have enough knowledge on how to modify this to work with a stacked bar chart.
How I want it to look like:
The way I ended up resolving this is:
var option = {
tooltip: {
trigger: 'item',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['Graph 1', 'Graph 2']
},
grid: {
left: '7%',
right: '5%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: [40, '', 41, '', 42, '', 43, '', 44],
axisLabel: {
interval: 1
},
splitLine: {
show: false
}
}
],
yAxis: [
{
type: 'value',
name: 'Y',
nameLocation: 'middle',
nameTextStyle: {
padding: [0, 0, 8, 0],
color: '#767676',
fontSize: 14
},
axisTick: {
show: false
},
axisLabel: {
show: false
},
splitLine: {
show: false
}
}
],
series: [
{
name: 'Graph 1',
type: 'bar',
barWidth: 20,
stack: 'Graph',
data: [8000, 0, 10000, 0, 12000, 0, 16000, 0, 20000],
markLine: {
silent: true,
symbol: 'none',
label: {
show: false
},
data: [
{
name: "Test",
xAxis: 2.5,
lineStyle: {
color: '#767676',
width: 2,
type: 'dashed'
}
},
]
}
},
{
name: 'Graph 2',
type: 'bar',
barWidth: 20,
barGap: '-100%',
stack: 'Graph',
data: [4000, 0, 5000, 0, 6000, 0, 8000, 0, 10000]
}
]
};
A few things to note:
Add a bunch of empty string entries for your labels (x-axis). As can be seen above, data is [40, '', 41, '', 42, '', 43, '', 44] instead of [40, 41, 42, 43, 44].
Add zeroes in your graph series relative to where you inserted empty string labels in your x-axis data above (e.g. 4000, 0, 5000, 0, 6000, 0, 8000, 0, 10000). Do this for each graph you have.
In any of the graphs, insert the above markLine syntax I added to Graph 1. You will need to adjust the value of xAxis until you get the middle point.
Demo: https://jsfiddle.net/k9hgj5zb/1/
Credits to this link: Draw horizontal target line using EChart.JS