I'm trying to apply ZoomIn and ZoomOut in a line chart on a mobile device. The goal is to click on a zone of the chart and ZoomIn in the first click and ZoomOut on the second. The sequence will always be this one.
I already live to see the documentation / examples and I can not find anything to solve this situation.
I have already tried using this properties in the chart: property
pinchType : 'y',
zoomType: 'none'
I tried the zoomtype but the behavior is not what I expect. I want to have a click to zoom this specific area of the chart. I do not want to zoom with two fingers.
{
chart: {
pinchType : 'x'
},
legend: {
itemStyle: {
color: '#fff'
}
},
plotOptions: {
series: {
animation: {
duration: 2000
}
}
},
xAxis: {
tickInterval: 1
},
series: [
{
type: 'spline',
color : '#fff'
},
{
dashStyle: 'longdash',
color: '#b3be77'
}
],
}
As simple as clicking to get zoomin and zoomout
Yes, the second challenge can be easily achieved by adding this logic to plotOptions.series.events.click callback function:
chart: {
events: {
load: function() {
this.clickedOnce = false;
},
click: function() {
const chart = this;
if (chart.clickedOnce) {
chart.zoomOut();
chart.clickedOnce = false;
}
}
}
},
plotOptions: {
series: {
events: {
click: function(e) {
const chart = this.chart,
yAxis = chart.yAxis[0],
xAxis = chart.xAxis[0];
let x,
y,
rangeX,
rangeY;
if (!chart.clickedOnce) {
x = xAxis.toValue(e.chartX);
y = yAxis.toValue(e.chartY);
rangeX = xAxis.max - xAxis.min;
rangeY = yAxis.max - yAxis.min;
xAxis.setExtremes(x - rangeX / 10, x + rangeX / 10, false);
yAxis.setExtremes(y - rangeY / 10, y + rangeY / 10, false);
chart.redraw();
chart.clickedOnce = true;
} else {
chart.zoomOut();
chart.clickedOnce = false;
}
}
}
}
}
Demos:
https://jsfiddle.net/BlackLabel/kotgea5n/
https://jsfiddle.net/BlackLabel/s8w2xg3e/1/
This functionality is not implemented in Highcharts by default, but you can easily achieve it by adding your custom logic when the chart area is clicked.
When area is clicked the first time use axis.setExtremes() method to zoom in. On the second click use chart.zoomOut() to zoom out the chart. Check demo and code posted below.
Code:
chart: {
events: {
load: function() {
this.clickedOnce = false;
},
click: function(e) {
const chart = this,
yAxis = chart.yAxis[0],
xAxis = chart.xAxis[0];
let x,
y,
rangeX,
rangeY;
if (!chart.clickedOnce) {
x = xAxis.toValue(e.chartX);
y = yAxis.toValue(e.chartY);
rangeX = xAxis.max - xAxis.min;
rangeY = yAxis.max - yAxis.min;
xAxis.setExtremes(x - rangeX / 10, x + rangeX / 10, false);
yAxis.setExtremes(y - rangeY / 10, y + rangeY / 10, false);
chart.redraw();
chart.clickedOnce = true;
} else {
chart.zoomOut();
chart.clickedOnce = false;
}
}
}
}
Demo:
https://jsfiddle.net/BlackLabel/fxm812k4/
API reference:
https://api.highcharts.com/class-reference/Highcharts.Axis#setExtremes
https://api.highcharts.com/class-reference/Highcharts.Chart#zoomOut
https://api.highcharts.com/highcharts/chart.events.click
Using a customEvents plugin (see: https://github.com/blacklabel/custom_events) and adding plotBand on the whole chart area you can register a callback on click and double click events. Using this approach you can make a zoom in on click event and zoom out on double click (not working on mobile devices).
Demo:
https://jsfiddle.net/BlackLabel/6tpb5q2z/
Related
I want to display legend for a line series at the top and colorAxis for heatmap at the right side in a chart. There is a global option to position the legend but no separate option for each series.
Highcharts Version: v4.3.1
That feature is not yet implemented in Highcharts core, but it should be available soon: https://github.com/highcharts/highcharts/issues/11309
For now you can move colorAxis from the legend to another place on the chart in this way:
(function(H) {
H.wrap(H.ColorAxis.prototype, 'drawLegendSymbol', function(proceed) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
this.legendItemWidth = 0;
this.legendItemHeight = 0;
});
}(Highcharts));
Highcharts.chart('container', {
chart: {
marginRight: 80,
events: {
load: function() {
var chart = this,
colorAxis = chart.colorAxis[0].axisParent.element,
mainSVG = chart.container.children[0],
xPos = chart.plotLeft + chart.plotWidth + 10,
yPos = chart.plotTop;
mainSVG.appendChild(colorAxis);
colorAxis.setAttribute(
'transform', 'translate(' + xPos + ', ' + yPos + ')'
)
}
}
},
series: [{
...,
showInLegend: true
}, {
...,
showInLegend: true
}],
colorAxis: {
layout: 'vertical'
}
});
Live demo: http://jsfiddle.net/BlackLabel/0c9fkvwp/
API Reference: https://api.highcharts.com/highcharts/chart.events
Docs: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts
I have a line graph with lot of points to plot
I want x axis to be scrollable
I have already looked few solutions but they are providing solution with old versions of chart js.
Is there any option to get scrollable x axis in chart.js version 2?
And
How can i get width of content in y axis in chart.js version 2?
if there is no direct option to get scrollable x axis, I can copy content in Y-axis region and draw image in other canvas.
My answer on a related question will help you. In my example I have made the Y axis scrollable, but this could easily be applied to the X axis too.
https://stackoverflow.com/a/51282003/10060003
JS fiddle - https://jsfiddle.net/EmmaLouise/eb1aqpx8/3/
I am using the animation onComplete and onProgress options to redraw the axis that I want to scroll with the chart. (See https://www.chartjs.org/docs/latest/configuration/animations.html).
$(function () {
var rectangleSet = false;
var canvasTest = $('#chart-Test');
var chartTest = new Chart(canvasTest, {
type: 'bar',
data: chartData,
maintainAspectRatio: false,
responsive: true,
options: {
tooltips: {
titleFontSize: 0,
titleMarginBottom: 0,
bodyFontSize: 12
},
legend: {
display: false
},
scales: {
xAxes: [{
ticks: {
fontSize: 12,
display: false
}
}],
yAxes: [{
ticks: {
fontSize: 12,
beginAtZero: true
}
}]
},
animation: {
onComplete: function () {
if (!rectangleSet) {
var scale = window.devicePixelRatio;
var sourceCanvas = chartTest.chart.canvas;
var copyWidth = chartTest.scales['y-axis-0'].width - 10;
var copyHeight = chartTest.scales['y-axis-0'].height + chartTest.scales['y-axis-0'].top + 10;
var targetCtx = document.getElementById("axis-Test").getContext("2d");
targetCtx.scale(scale, scale);
targetCtx.canvas.width = copyWidth * scale;
targetCtx.canvas.height = copyHeight * scale;
targetCtx.canvas.style.width = `${copyWidth}px`;
targetCtx.canvas.style.height = `${copyHeight}px`;
targetCtx.drawImage(sourceCanvas, 0, 0, copyWidth * scale, copyHeight * scale, 0, 0, copyWidth * scale, copyHeight * scale);
var sourceCtx = sourceCanvas.getContext('2d');
// Normalize coordinate system to use css pixels.
sourceCtx.clearRect(0, 0, copyWidth * scale, copyHeight * scale);
rectangleSet = true;
}
},
onProgress: function () {
if (rectangleSet === true) {
var copyWidth = chartTest.scales['y-axis-0'].width;
var copyHeight = chartTest.scales['y-axis-0'].height + chartTest.scales['y-axis-0'].top + 10;
var sourceCtx = chartTest.chart.canvas.getContext('2d');
sourceCtx.clearRect(0, 0, copyWidth, copyHeight);
}
}
}
}
});
Considering that my data array is always composed of 4 elements as :
var data = [
{"type":"column","name":"My Label 1","y":38.9500000000003,"color":"#7cb342"},
{"type":"column","name":"My Label 2","y":30,"color":"#7cb342"}, {"type":"column","name":"My Label 3","y":51.85,"color":"#fbc02d"}, {"type":"column","name":"My Label 4","y":55.2999999999997,"color":"#fbc02d"}];
I want to know how to set my data names (data.name) each 45 degrees tick interval to keep them well positioned ?
Here is the example:
http://jsfiddle.net/eento/7rupgxde/4/
It's important for me to display only those labels & keep them inside the global highchart container.
Like this?
dataLabels: {
enabled: true,
formatter: function() {
return this.point.name;
}
}
Example:
http://jsfiddle.net/jlbriggs/7rupgxde/5/
You can use renderer to render those labels, for example, create two methods (one to add labels and one to position them):
function renderLabels(chart) {
var alignments = ['right', 'right', 'left', 'left'];
$.each(chart.series[0].points, function(i, point) {
point.myName = chart.renderer.text(point.name, -9999, -9999).attr({
align: alignments[i],
color: 'black'
}).add();
});
}
function positionLabels(chart, anim) {
var positions = [
// top right label
[chart.plotLeft + chart.plotWidth, chart.plotTop],
// bottom right label
[chart.plotLeft + chart.plotWidth, chart.plotTop + chart.plotHeight],
// bottom left label
[chart.plotLeft, chart.plotTop + chart.plotHeight],
// top left label
[chart.plotLeft, chart.plotTop],
]
$.each(chart.series[0].points, function(i, point) {
if (point.myName) {
point.myName[(anim ? 'animate' : 'attr')]({
x: positions[i][0],
y: positions[i][1],
})
}
});
}
Then use those methods in chart.events:
chart: {
polar: true,
renderTo: 'container',
backgroundColor: null,
events: {
load: function() {
renderLabels(this);
positionLabels(this, false);
},
redraw: function() {
positionLabels(this, true);
}
}
},
And working demo: http://jsfiddle.net/7rupgxde/7/
I'm wondering if there is a way to apply dynamic settings to individual marker of an highstock chart? I've searched for half a day and I have the feeling that there is a problem with the API. It seems that there is no ways to adjust marker setting on a specific datum. ex:
$('#container').highcharts('StockChart', {
chart : {
events : {
load : function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.round(Math.random() * 100);
series.addPoint([x, y], true, true);
}, 1000);
}
}
},
series : [{
data : (function () {
var data = [], time = (new Date()).getTime(), i;
for (i = -999; i <= 0; i += 1) {
data.push([
{ x: time + i * 1000,
y: Math.round(Math.random() * 100),
marker:{
fillColor:'red'
}
}
]);
}
return data;
}())
}]
}
I've fork a basic Highstock demo to illustrate my point. See the jsfiddle that demonstrate the problem: http://jsfiddle.net/9xj0nz72/1/
Maybe I have an error in my fiddle... or may I have to create an issue on Github?
Thanks a lot!!
I had to assign the style in the addPoint method, you can't just push to the data array. And you have to use it on the chart = new Highcharts.StockChart() variable.
I'm pretty sure I got what you were hoping for using the following. And to demonstrate I assigned a random color and radius to each new point.
$(function () {
var chart = new Highcharts.StockChart({
chart: {
renderTo: 'container'
},
plotOptions: {
series: {
marker: {
enabled: true
}
}
},
series: [{
name: 'Random data',
data: [],
time: (new Date()).getTime()
}]
});
/* add new random point every 1 second */
var i = 0;
setInterval(function () {
i++;
chart.series[0].addPoint({
marker: {
/* assign a random hex color and radius */
fillColor: '#' + (Math.random() * 0xFFFFFF << 0).toString(16),
radius: Math.floor(Math.random() * 10) + 1
},
y: Math.random() * 100,
x: i * 1000,
}, true, false);
}, 1000);
});
Your updated JSFiddle
The problem I am encountering is that the black line is doing some funky stuff compared to the blue line. If you scroll in the middle or somewhere else (use bottom scroll tool), you can clearly see that the black line is changing its shape, while the blue line holds its shape, strange. This only happens when you use the scroll tool.
How can I prevent the black line changing its shape? Copy this code and replace it with the JSfiddle to see the problem:
$(function () {
Highcharts.setOptions({
global : {
useUTC : false
}
});
// Create the chart
$('#container').highcharts('StockChart', {
chart : {
events : {
load : function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.round(Math.random() * 100);
series.addPoint([x, y], true, true);
}, 1000);
var series1 = this.series[1];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.round(Math.random() * 100);
series1.addPoint([x, y], true, true);
}, 1000);
}
}
},
rangeSelector: {
buttons: [{
count: 1,
type: 'minute',
text: '1M'
}, {
count: 5,
type: 'minute',
text: '5M'
}, {
type: 'all',
text: 'All'
}],
inputEnabled: false,
selected: 0
},
title : {
text : 'Live random data'
},
exporting: {
enabled: false
},
series : [{
name : 'diagram1',
data : (function () {
// generate an array of random data
var data1 = [], time = (new Date()).getTime(), i;
for (i = -999; i <= 0; i += 1) {
data1.push([
time + i * 1000,
Math.round(Math.random() * 100)
]);
}
return data1;
}())
},
{
name : 'diagram2',
data : (function () {
// generate an array of random data
var data2 = [], time = (new Date()).getTime(), i;
for (i = -999; i <= 0; i += 1) {
data2.push([
time + i * 1000,
Math.round(Math.random() * 100)
]);
}
return data2;
}())
}]
});
});
The only thing I have done is to add an extra dynamic line (black one) to the diagram. Here is the original code without the black line. Review original code
The reason of the movement in the black line is the redraw and the animation function of the chart. For some reason the first series(blue line) doesn't show animation after calling addPoint so you don't see the movement. The second parameter in the addPoint function is redraw. Setting it to false for the second series(black line) will stop the movement when updating the points:
series1.addPoint([x, y], false, true);
Here's the DEMO.