Stacked axes turned on their side: three panes next to each other, each with its own value axis, and one list of names down the left for all three. Here, a sales team’s deals, calls and revenue per person.
Side-by-side panes suit long lists of names: they run down the left once, and each measure gets its own scale and its own column of space. Reading across a row gives one person’s three numbers; reading down a pane compares everyone on one measure.
Built with amCharts 5, a JavaScript library. The code includes your theme and renderer (canvas or SVG), but not the changes you make with the controls.
npm install @amcharts/amcharts5. See Getting started.
The same chart as a JSON config. Chart Maker reads and writes this format, the library's JSON parser turns it into a chart, and the <am5-chart> element puts it on a page with no code."type" is the class to create and "settings" what you would pass to its new() in code; "properties" fill in its parts, lists such as series and axes or templates such as columns.template; "refs" holds objects used in more than one place ("#series-0" stands for the one named series-0); a string starting with "@" takes a value from another object, such as "@series.get('fill')".
A complete page. Save it as an .html file and open it in a browser.
Paste this into an AI coding assistant to build the chart from scratch. More in amCharts & AI.
// Made-up quarter of a sales team: deals closed (around 20), calls made (around 200) and revenue in
// thousands of dollars (around 2,000) per person
var data = [];
var value1 = 20;
var value2 = 200;
var value3 = 2000;
var names = [
"Raina",
"Demarcus",
"Carlo",
"Jacinda",
"Richie",
"Antony",
"Amada",
"Idalia",
"Janella",
"Marla",
"Curtis",
"Shellie",
"Meggan",
"Nathanael",
"Jannette",
"Tyrell",
"Sheena",
"Maranda",
"Briana"
];
// each person's numbers are within 20% of the previous person's
for (var i = 0; i < names.length; i++) {
value1 += Math.round(
(Math.random() < 0.5 ? 1 : -1) * Math.random() * value1 * 0.2
);
value2 += Math.round(
(Math.random() < 0.5 ? 1 : -1) * Math.random() * value2 * 0.2
);
value3 += Math.round(
(Math.random() < 0.5 ? 1 : -1) * Math.random() * value3 * 0.2
);
data.push({
category: names[i],
value1: value1,
value2: value2,
value3: value3
});
}
// Create root element
// https://www.amcharts.com/docs/v5/getting-started/#Root_element
var root = am5.Root.new("chartdiv");
// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([
am5themes_Animated.new(root),
am5themes_Responsive.new(root)
]);
// Create chart
// https://www.amcharts.com/docs/v5/charts/xy-chart/
var chart = root.container.children.push(
am5xy.XYChart.new(root, {
panX: false, // no panning sideways
panY: true, // drag up or down to pan the names
wheelX: "none",
wheelY: "zoomY", // the wheel zooms in on the names
// each tooltip stays at its own point: side by side, the panes' tooltips don't collide
arrangeTooltips: false,
pinchZoomY: true // pinch with two fingers to zoom on touch screens
})
);
// make x axes stack
chart.bottomAxesContainer.set("layout", root.horizontalLayout); // the x axes sit side by side, one per pane
// Create axes
// https://www.amcharts.com/docs/v5/charts/xy-chart/axes/
var yRenderer = am5xy.AxisRendererY.new(root, {
minGridDistance: 15 // names may sit 15px apart, so most get a label
});
yRenderer.labels.template.setAll({
multiLocation: 0.5, // a label covering several names sits in the middle of them
location: 0.5, // each name in the middle of its row
paddingRight: 15 // 15px between the names and the plot
});
// grid lines through the middle of each row, where the points and bars are
yRenderer.grid.template.set("location", 0.5);
var yAxis = chart.yAxes.push(
am5xy.CategoryAxis.new(root, {
categoryField: "category",
tooltip: am5.Tooltip.new(root, {}), // shows the hovered name on the axis
renderer: yRenderer
})
);
yAxis.data.setAll(data);
// Add series
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
// one pane: a value axis with its title, and a column or line series on it
function createSeries(field, name, margin, column) {
var xRenderer = am5xy.AxisRendererX.new(root, {
minGridDistance: 40 // at least 40px between value labels
});
xRenderer.labels.template.setAll({
rotation: -90, // the labels read upward...
centerY: am5.p50 // ...centered on their grid lines
});
var xAxis = chart.xAxes.push(
am5xy.ValueAxis.new(root, {
renderer: xRenderer,
tooltip: am5.Tooltip.new(root, {
animationDuration: 0 // the axis tooltip jumps straight to the cursor
}),
// the same height for all three, so the pane titles line up
height: 60,
marginLeft: margin // this makes gap between axes
})
);
// columns start at zero, so their lengths compare
if (column) {
xAxis.set("min", 0);
}
// name the pane with a title under its axis
xAxis.children.push(am5.Label.new(root, {
text: name,
x: am5.p50, // centered...
centerX: am5.p50 // ...by its own middle
}));
var series;
if (column) {
series = chart.series.push(
am5xy.ColumnSeries.new(root, {
name: name,
xAxis: xAxis,
yAxis: yAxis,
valueXField: field,
categoryYField: "category",
sequencedInterpolation: true, // on load, the bars grow one after another
tooltip: am5.Tooltip.new(root, {
pointerOrientation: "horizontal", // the tooltip points sideways at the bar
labelText: "{name}: {valueX}" // as "Deals closed: 20"
})
})
);
} else {
series = chart.series.push(
am5xy.LineSeries.new(root, {
name: name,
xAxis: xAxis,
yAxis: yAxis,
valueXField: field,
categoryYField: "category",
sequencedInterpolation: true, // on load, the points move in one after another
tooltip: am5.Tooltip.new(root, {
pointerOrientation: "horizontal",
labelText: "{name}: {valueX}"
})
})
);
}
if (!column) {
series.bullets.push(function () {
return am5.Bullet.new(root, {
locationX: 1,
locationY: 0.5, // the dot in the middle of its row
sprite: am5.Circle.new(root, {
radius: 4, // 4px radius
fill: series.get("fill") // in the line's color
})
});
});
}
series.data.setAll(data);
series.appear();
return series;
}
createSeries("value1", "Deals closed", 0, true); // columns, no gap on the left
createSeries("value2", "Calls made", 30, false); // a line, 30px from the pane before
createSeries("value3", "Revenue, $k", 30, false);
// Add cursor
// https://www.amcharts.com/docs/v5/charts/xy-chart/cursor/
var cursor = chart.set("cursor", am5xy.XYCursor.new(root, {
behavior: "none", // a drag over the plot pans instead of zooming
yAxis: yAxis
}));
// Make stuff animate on load
// https://www.amcharts.com/docs/v5/concepts/animations/
chart.appear(1000, 100);