Related
I am using Chartjs 4.0.1 and chartjs-plugin-zoom 2.0.0 and my chart look like this:
I have set the drag option to be enabled so the user can draw a rectangle to zoom in. Also I have set the zoom mode to 'x'. So the user can only zoom in on the x axis but not on the y axis.
Now I want to limit how far the user can zoom in, to a timespan of one month. I have managed to do that when using the mousewheel to zoom in. But I dont know how to achive the same when using the drag option. I have it configured like this:
drag:{
enabled: true,
backgroundColor:'rgba(180,180,180,0.4)',
threshold: 25,
}
The threshold seems to be my best option to a limit. However that is in pixels and it only says how wide the drawn rectangle has to be for a zoom to occur.
I am already using the onZoomStart callback to check how far the chart is zoomed in and based on that decide if the user can zoom in even more. But apparently that callback is only executed when zooming by mousewheel but not when dragging. So I think I would need to be able to set the threshold of the drag object dynamically. Does anyone know how to do that?
Also I was wondering, is it possible to change the border color of the rectangle when dragging to show the user if it is big enough for a scroll to occur?
The standard solution seems to be to set a limits:{x:{minRange:...}} option. It took me a while to realise where that option should be inserted.
Below is a code snippet with some data resembling yours and a minRange set to 90 days (so I can skip adjusting the tick interval).
Also, there's a hack that changes the color of the drag rectangle to red if the interval is less than the 90 days. It can easily be adapted to completely reject the zoom for less than the desired interval, instead of the current standard behavior which is to adjust (extend) the interval until it is equal to minRange.
The same in this fiddle.
const nPoints = 400,
t0 = Date.parse("2018-06-02T00:00:00Z"),
dt = 2.5*365/nPoints*24*3600*1000;
const data = Array.from(
{length: nPoints},
(_, i)=>({
"timestamp":(t0+dt*i),
value: 80*Math.sin(i*Math.PI/nPoints)+2*Math.random()
})
);
let mouseMoveHandler = null;
chart = new Chart(document.getElementById("myChart"), {
type: 'line',
data: {
datasets: [{
label: "Count",
//pointStyle: false,
pointRadius: 2,
showLine: true,
fill: true,
tension: 0,
borderColor: '#aa6577',
//pointRadius: 4,
//pointBorderWidth: 1,
//pointBackgroundColor: '#7265ce',
data: data
}]
},
options: {
parsing: {
xAxisKey: 'timestamp',
yAxisKey: 'value'
},
spanGaps: false,
responsive: false,
scales: {
x: {
bounds: 'ticks',
type: 'time',
time: {
unit: 'month',
},
title: {
display: false,
text: 'time'
},
ticks: {
display: true,
color: '#cecece'
}
},
y: {
type: 'linear',
display: true,
min: -10,
max: 140,
ticks: {
autoSkip: true,
color: '#cecece'
},
grid:{
color: ctx => ctx.tick.value === 0 ? '#000' : '#ddd',
lineWidth: ctx => ctx.tick.value === 0 ? 3 : 1,
},
title: {
display: false,
text: 'Count',
align: 'end'
},
}
},
plugins:{
legend:{
display: false
},
zoom: {
zoom: {
drag: {
enabled: true,
backgroundColor:'rgba(180,180,180,0.4)',
},
mode: 'x',
onZoomStart({chart, event}){
const x0 = chart.scales.x.getValueForPixel(event.clientX);
if(event.type==="mousedown"){
mouseMoveHandler = function(e){
if(
Math.abs(chart.scales.x.getValueForPixel(e.clientX) - x0) <
chart.options.plugins.zoom.limits.x.minRange
){
chart.options.plugins.zoom.zoom.drag.backgroundColor = 'rgba(255,180,180,0.4)';
}
else{
chart.options.plugins.zoom.zoom.drag.backgroundColor = 'rgba(180,180,180,0.4)';
}
};
chart.canvas.addEventListener("mousemove", mouseMoveHandler);
chart.canvas.addEventListener("mouseup", function(){
if(mouseMoveHandler){
chart.canvas.removeEventListener("mousemove", mouseMoveHandler);
mouseMoveHandler = null;
}
}, {once: true});
}
},
onZoomComplete({chart}){
if(mouseMoveHandler){
chart.canvas.removeEventListener("mousemove", mouseMoveHandler);
mouseMoveHandler = null;
}
document.querySelector('#zoom').innerText = chart.getZoomLevel().toFixed(1)+'x';
document.querySelector('#xSpan').innerText =
Math.round((chart.scales.x.max-chart.scales.x.min)/24/3600/1000)+'days';
}
},
limits:{
x: {
minRange: 90 * 24* 3600 * 1000
}
}
}
}
}
});
document.querySelector('#resetZoom').addEventListener('click', function(){chart.resetZoom();});
document.querySelector('#xSpan').innerText = Math.round((chart.scales.x.max-chart.scales.x.min)/24/3600/1000)+'days';
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.0.1/chart.umd.min.js"
integrity="sha512-HyprZz2W40JOnIBIXDYHCFlkSscDdYaNe2FYl34g1DOmE9J+zEPoT4HHHZ2b3+milFBtiKVWb4sorDVVp+iuqA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/2.0.0/chartjs-plugin-zoom.min.js"
integrity="sha512-B6F98QATBNaDHSE7uANGo5h0mU6fhKCUD+SPAY7KZDxE8QgZw9rewDtNiu3mbbutYDWOKT3SPYD8qDBpG2QnEg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js">
</script>
<canvas id="myChart" style="height:500px; width: 90vw"></canvas>
<button id="resetZoom">Reset zoom</button> <br>
zoom: <span id="zoom">1x</span><br>
X axis span: <span id="xSpan"></span>
I am using ng-apexcharts, and I want to change only quarter of the background to another color like in the following example:
enter image description here
Currently using annotations with offestY, but we cant understand what is the offestY value and how we can make it 50% of the graph size.
Code example:
chart: {
type: 'bubble',
height: auto,
...
},
annotations: {
xaxis: [
{
x: 50,
x2: 100,
fillColor: '#f15252',
opacity: 0.1,
offsetY: -70, // How to calculate this value to be exactly 50% offest?
}
],
},
thanks!
I found a solution, maybe it will help someone:
events: {
mounted: (chart, options) => {
const offsetX = options.globals.gridWidth / 2;
this.chart.addYaxisAnnotation({
y: 5,
y2: 10,
fillColor: '#f15252',
opacity: 0.1,
offsetX,
});
},
}
How do I adjust the column width on a google combo chart? Below is my code, but I can't figure out how to set the column width. Depending on the data I enter, the api makes the columns different widths. I'd like them all 10px. I've been trying to set the with with bar.groupWidth but cannot. Any ideas?
<script type="text/javascript">
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart);
function getValueAt(column, dataTable, row) {
return dataTable.getFormattedValue(row, column);
}
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Time', 'Boluses', 'Total Volume', '30 mL/kg', { role: 'annotation' }], [0,0,0,1769.1, null],[9, 500, 500, 1769.1, null],[29, 250, 750, 1769.1, null],[44, 250, 1000, 1769.1, null],[114, 2000, 3000, 1769.1, null],[238, 0, 3000, 1769.1, null],[238, 0, 3000, 1769.1, null],[288, 85, 3085, 1769.1, null],[288, 6.8, 3091.8, 1769.1, null],[348, 100, 3191.8, 1769.1, null],[348, 8, 3199.8, 1769.1, null],[408, 100, 3299.8, 1769.1, null],[408, 8, 3307.8, 1769.1, null],[360, 0, 3307.8, 1769.1, null]
]);
var view = new google.visualization.DataView(data);
var options = {
title: 'sepsis treatment summary',
fontName: 'Lato',
titleTextStyle: {fontSize: 18},
annotation: {},
vAxis: {title: 'total fluids received (mL)', minValue: 0, gridlines: {count: 6}},
hAxis: {title: 'time after alert (minutes)', viewWindow: {min: 0, max: 360}, gridlines: {count: 6}},
seriesType: "bars",
series: {
1: {color: '#99CCFF', type: "area"},
2: {color: 'red', type: "line", lineDashStyle: [10, 2]},
3: {role: "annotation"}
},
annotations: {style: 'line'},
};
var chart = new google.visualization.ColumnChart(document.getElementById('chart'));
chart.draw(view, options);
}
This code creates the following chart:
The API calculates a maximum width for each bar group that is roughly:
var chartWidth, // chart area width in pixels
axisRange, // axis max value - axis min value
data, // DataTable, assume axis values are in column 0 for this exercise, and that data is sorted ascending
minSeparation = axisRange, // will hold the minimum separation between daat points
barGroupWidth;
// calculate the minimum distance between any two adjacent points
for (var i = 1; i < data.getNumberOfRows(); i++) {
if (data.getValue(i, 0) - data.getValue(i - 1, 0) < minSeparation) {
minSeparation = data.getValue(i, 0) - data.getValue(i - 1, 0);
}
}
// calculate the maximum width of a bar group
barGroupWidth = chartWidth * minSeparation / axisRange;
Pleaase note that this function is a rough approximation of what the API does based on what I was able to reverse engineer.
So, if you have a chart that has a chart area 300 pixels wide with an axis range of 100 and a minimum separation between adjacent points of 10, your maximum bar group width will be 30 pixels. If you try to set the bar group width above this value, your setting will be ignored.
In your case, you have adjacent points with a separation of 0 (rows 5 and 6, 7 and 8, 9 and 10, 11 and 12), which would result in a bar group width of 0 by my rough approximation. The actual algorithm is more generous, and is likely giving you 1 pixel wide groups. There is no setting you can change to make the bar groups wider, your only recourse is to change your data set to space the values out more. This may not be easy to do, but I would suggest starting by thinking about what it means to have two events at the same time with different values, and how you might be able to represent that differently.
I'm having trouble keeping the same level of opacity on an element with a gradient filled color
var paper = Raphael(0, 0, 300, 300);
paper.path(["M", 20, 20, "h", 200, "v", 200, "h", -200, "z"]).attr({
"stroke-width": 3,
stroke: 'red',
"opacity": 0.5,
fill: "90-red-red"
});
http://jsfiddle.net/zhirkovski/vvAaz/1/
as you can see the gradient starts off at 0.5, but increases to 1 by the time it reaches the second color, why? Even if you change the colors, one of them renders at opacity = 1, is this a bug? if so is there a work-around, or is it something i'm doing wrong?
From my own investigation this looks like a limitation of VML and subsequently Raphael. You can find more information via the following bug report: https://github.com/DmitryBaranovskiy/raphael/issues/211
It really limits what you can do with gradients and fades which is a bane for all of us. The best way to do this would be with jQuery:
// Setting up defaults
var paper = Raphael("canvas", 200, 200);
var bgBottom = paper.rect(0, 0, 200, 200).attr({fill: "90-#999-#fff"});
var bgTop = paper.rect(0, 0, 200, 200).attr({fill: "90-#999-#fff"});
// New gradient to fade to
bgBottom.attr({fill: "90-#069-#000"});
$(bgTop.node).animate({opacity: 0}, 1000);
You can then animate the top in and out with fill changes:
bgTop.attr({fill: "90-#f0f-#fff"});
$(bgTop.node).animate({opacity: 1}, 1000);
Here's my jsfiddle to help demonstrate: http://jsfiddle.net/spQsf/
Hope this helps!
I'm trying to implement the Raphael image rotation demo:
http://raphaeljs.com/image-rotation.html
as a slider within the Foundation framework, but I can't get it to centre without cutting the sides off when it rotates, please see here
http://jcfolio.co.uk/current/index-raphael.html
<script>
window.onload = function () {
var src = document.getElementById("bee").src,
angle = 0;
document.getElementById("globe-holder").innerHTML = "";
var R = Raphael("globe-holder", 1000 , 500);
var img = R.image(src,-535, -150, 2000, 2000);
var butt1 = R.set(),
butt2 = R.set();
butt1.push(R.circle(24.833, 26.917, 26.667).attr({stroke: "none", fill: "#66c100", "fill-opacity": .5 }),
R.path("M12.582,9.551C3.251,16.237,0.921,29.021,7.08,38.564l-2.36,1.689l4.893,2.262l4.893,2.262l-0.568-5.36l-0.567-5.359l-2.365,1.694c-4.657-7.375-2.83-17.185,4.352-22.33c7.451-5.338,17.817-3.625,23.156,3.824c5.337,7.449,3.625,17.813-3.821,23.152l2.857,3.988c9.617-6.893,11.827-20.277,4.935-29.896C35.591,4.87,22.204,2.658,12.582,9.551z").attr({stroke: "none", fill: "#fff"}),
R.circle(24.833, 26.917, 26.667).attr({fill: "#fff", opacity: 0, cursor: "pointer" }));
butt2.push(R.circle(24.833, 26.917, 26.667).attr({stroke: "none", fill: "#66c100", "fill-opacity": .5 }),
R.path("M37.566,9.551c9.331,6.686,11.661,19.471,5.502,29.014l2.36,1.689l-4.893,2.262l-4.893,2.262l0.568-5.36l0.567-5.359l2.365,1.694c4.657-7.375,2.83-17.185-4.352-22.33c-7.451-5.338-17.817-3.625-23.156,3.824C6.3,24.695,8.012,35.06,15.458,40.398l-2.857,3.988C2.983,37.494,0.773,24.109,7.666,14.49C14.558,4.87,27.944,2.658,37.566,9.551z").attr({stroke: "none", fill: "#fff"}),
R.circle(24.833, 26.917, 26.667).attr({fill: "#fff", opacity: 0, cursor: "pointer" }));
butt1.translate(35, 380);
butt2.translate(850, 380);
butt1[2].click(function () {
angle -= 120;
img.stop().animate({transform: "r" + angle}, 1000, "backOut");
}).mouseover(function () {
butt1[1].animate({fill: "#66c100"}, 300);
}).mouseout(function () {
butt1[1].stop().attr({fill: "#fff"});
});
butt2[2].click(function () {
angle += 120;
img.animate({transform: "r" + angle}, 1000, "backOut");
}).mouseover(function () {
butt2[1].animate({fill: "#66c100"}, 300);
}).mouseout(function () {
butt2[1].stop().attr({fill: "#fff"});
});
// setTimeout(function () {R.safari();});
};
</script>
When I increase the globe-holder width to 2000px (the full width of the png), it won't centre!
Think it's probably something simple I've missed! Any help greatly appreciated.
James