A line chart you can edit by hand: press anywhere on the plot and drag up or down, and the nearest point follows. Handy for what-if plans and rough forecasts.
Dragging points turns a chart into an input: people shape a forecast, a budget or a target curve by eye and see the result as they go. The new values are in the chart’s data, ready for your code to save or send on.
Built with amCharts 5, a JavaScript library. The JavaScript includes your theme and renderer (canvas or SVG); the JSON also follows 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.
// Create root element
// https://www.amcharts.com/docs/v5/getting-started/#Root_element
var root = am5.Root.new("chartdiv");
const myTheme = am5.Theme.new(root); // a theme of our own, for the rule below
myTheme.rule("AxisLabel").setAll({
fontSize:"0.9em" // axis labels at 90% of the normal text size
});
// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([
am5themes_Animated.new(root),
myTheme,
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, {
wheelX: "panX", // a horizontal wheel or trackpad swipe pans...
wheelY: "zoomX", // ...and the vertical wheel zooms in on the dates
pinchZoomX: true, // pinch with two fingers to zoom on a touch screen
paddingLeft: 0 // the value labels sit at the chart's left edge
}));
// 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 on the plot neither zooms nor selects, so it can move points
}));
cursor.lineY.set("visible", false); // only the vertical cursor line, no horizontal one
// Generate random data
var date = new Date();
date.setHours(0, 0, 0, 0);
var value = 100;
// one day's point: the value moves randomly by up to 5 from the day before
function generateData() {
value = Math.round((Math.random() * 10 - 5) + value);
am5.time.add(date, "day", 1);
return {
date: date.getTime(),
value: value
};
}
// an array of count daily points
function generateDatas(count) {
var data = [];
for (var i = 0; i < count; ++i) {
data.push(generateData());
}
return data;
}
// Create axes
// https://www.amcharts.com/docs/v5/charts/xy-chart/axes/
var xAxis = chart.xAxes.push(am5xy.DateAxis.new(root, {
maxDeviation: 0.2, // pans up to 20% past the first and last date
baseInterval: { // one data point per day
timeUnit: "day",
count: 1
},
renderer: am5xy.AxisRendererX.new(root, {
minorGridEnabled: true // fainter grid lines between the labeled ones
}),
tooltip: am5.Tooltip.new(root, {}) // a date label follows the cursor along the axis
}));
var yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
renderer: am5xy.AxisRendererY.new(root, {
// drag along the value axis labels to zoom it in and out
pan: "zoom"
})
}));
// Add series
// https://www.amcharts.com/docs/v5/charts/xy-chart/series/
var series = chart.series.push(am5xy.LineSeries.new(root, {
name: "Series",
xAxis: xAxis,
yAxis: yAxis,
valueYField: "value",
valueXField: "date",
tooltip: am5.Tooltip.new(root, {
labelText: "{valueY.formatNumber('#.#')}" // the value with up to one decimal
})
}));
// a hollow dot on each point, with an up-down pointer to hint it can be dragged
series.bullets.push(function() {
var graphics = am5.Circle.new(root, {
radius: 4, // 4px radius
interactive: true, // reacts to the pointer, so the pointer style shows
cursorOverStyle: "ns-resize", // an up-down arrow pointer over the dot
stroke: series.get("stroke"), // outlined in the line's color
// filled with the chart's background color, so the points look hollow in light and dark mode
fill: root.interfaceColors.get("background")
});
return am5.Bullet.new(root, {
sprite: graphics
});
});
// Add scrollbar
// https://www.amcharts.com/docs/v5/charts/xy-chart/scrollbars/
chart.set("scrollbarX", am5.Scrollbar.new(root, {
orientation: "horizontal" // a bar above the plot to zoom and scroll through the dates
}));
// manipulating with mouse code
var isDown = false; // true while the mouse button is held down on the plot
// register down
chart.plotContainer.events.on("pointerdown", function() {
isDown = true;
})
// register up
chart.plotContainer.events.on("globalpointerup", function() {
isDown = false;
})
// while the button is held, the point under the cursor follows the pointer up and down
chart.plotContainer.events.on("globalpointermove", function(e) {
// if pointer is down
if (isDown) {
// get tooltip data item
var tooltipDataItem = series.get("tooltipDataItem");
if (tooltipDataItem) {
if (e.originalEvent) {
// the pointer's height in the plot area, turned into a value on the Y axis
var position = yAxis.coordinateToPosition(chart.plotContainer.toLocal(e.point).y);
// rounded to one decimal
var value = Math.round(yAxis.positionToValue(position) * 10) / 10;
// need to set both working and original value
tooltipDataItem.set("valueY", value);
tooltipDataItem.set("valueYWorking", value);
}
}
}
})
// Set data
var data = generateDatas(40);
series.data.setAll(data);
// Make stuff animate on load
// https://www.amcharts.com/docs/v5/concepts/animations/
series.appear(1000);
chart.appear(1000, 100);
{
"type": "XYChart",
"settings": {
"wheelX": "panX",
"wheelY": "zoomX",
"pinchZoomX": true,
"paddingLeft": 0,
"cursor": {
"type": "XYCursor",
"settings": {
"behavior": "none"
},
"properties": {
"lineY": {
"settings": {
"visible": false
}
}
}
},
"scrollbarX": {
"type": "Scrollbar",
"settings": {
"orientation": "horizontal"
}
}
},
"properties": {
"series": [
"#series-0"
],
"xAxes": [
"#axis-0"
],
"yAxes": [
"#axis-1"
]
},
"refs": [
{
"data-0": [
{
"date": 1791406800000,
"value": 104
},
{
"date": 1791493200000,
"value": 107
},
{
"date": 1791579600000,
"value": 104
},
{
"date": 1791666000000,
"value": 101
},
{
"date": 1791752400000,
"value": 99
},
{
"date": 1791838800000,
"value": 99
},
{
"date": 1791925200000,
"value": 97
},
{
"date": 1792011600000,
"value": 99
},
{
"date": 1792098000000,
"value": 96
},
{
"date": 1792184400000,
"value": 93
},
{
"date": 1792270800000,
"value": 92
},
{
"date": 1792357200000,
"value": 94
},
{
"date": 1792443600000,
"value": 90
},
{
"date": 1792530000000,
"value": 88
},
{
"date": 1792616400000,
"value": 84
},
{
"date": 1792702800000,
"value": 85
},
{
"date": 1792789200000,
"value": 87
},
{
"date": 1792875600000,
"value": 86
},
{
"date": 1792965600000,
"value": 89
},
{
"date": 1793052000000,
"value": 86
},
{
"date": 1793138400000,
"value": 84
},
{
"date": 1793224800000,
"value": 82
},
{
"date": 1793311200000,
"value": 86
},
{
"date": 1793397600000,
"value": 85
},
{
"date": 1793484000000,
"value": 89
},
{
"date": 1793570400000,
"value": 90
},
{
"date": 1793656800000,
"value": 90
},
{
"date": 1793743200000,
"value": 92
},
{
"date": 1793829600000,
"value": 91
},
{
"date": 1793916000000,
"value": 87
},
{
"date": 1794002400000,
"value": 91
},
{
"date": 1794088800000,
"value": 89
},
{
"date": 1794175200000,
"value": 89
},
{
"date": 1794261600000,
"value": 91
},
{
"date": 1794348000000,
"value": 92
},
{
"date": 1794434400000,
"value": 96
},
{
"date": 1794520800000,
"value": 96
},
{
"date": 1794607200000,
"value": 97
},
{
"date": 1794693600000,
"value": 94
},
{
"date": 1794780000000,
"value": 91
}
]
},
{
"axis-0": {
"type": "DateAxis",
"settings": {
"maxDeviation": 0.2,
"baseInterval": {
"timeUnit": "day",
"count": 1
},
"renderer": {
"type": "AxisRendererX",
"settings": {
"minorGridEnabled": true
}
},
"tooltip": {
"type": "Tooltip"
}
}
}
},
{
"axis-1": {
"type": "ValueAxis",
"settings": {
"renderer": {
"type": "AxisRendererY",
"settings": {
"pan": "zoom"
}
}
}
}
},
{
"series-0": {
"type": "LineSeries",
"settings": {
"name": "Series",
"xAxis": "#axis-0",
"yAxis": "#axis-1",
"valueYField": "value",
"valueXField": "date",
"tooltip": {
"type": "Tooltip",
"settings": {
"labelText": "{valueY.formatNumber('#.#')}"
}
}
},
"properties": {
"data": "#data-0",
"bullets": [
{
"type": "Bullet",
"settings": {
"sprite": {
"type": "Circle",
"settings": {
"radius": 4,
"interactive": true,
"cursorOverStyle": "ns-resize",
"stroke": "@series.get('stroke')",
"fill": "@root.interfaceColors.get('background')"
}
}
}
}
]
}
}
}
]
}