Related
I have a line chart in chart js. I want to give it a different background on the y axis say, 0-40 is red,40-70 is yellow and 70-100 is green. The limit for the y axis will always be 100.
var scatterChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: ' Dataset',
data: [{
x: 1,
y: 10
}, {
x: 2,
y: 50
}, {
x: 3,
y: 88
}, {
x: 4,
y: 5
}]
}]
},
options: {
scales: {
xAxes: [{
type: 'linear',
position: 'bottom'
}]
}
}
});
How do i set the background?
There is not a built in option, but we can achieve the result with some code.
var ctx = document.getElementById("chart").getContext("2d");
var scatterChart = new Chart(ctx, {
type: "line",
data: {
datasets: [{
label: " Dataset",
data: [{
x: 1,
y: 10
},
{
x: 2,
y: 50
},
{
x: 3,
y: 88
},
{
x: 4,
y: 5
}
]
}]
},
options: {
backgroundRules: [{
backgroundColor: "red",
yAxisSegement: 40
},
{
backgroundColor: "yellow",
yAxisSegement: 70
},
{
backgroundColor: "green",
yAxisSegement: Infinity
}
],
scales: {
xAxes: [{
type: "linear",
position: "bottom"
}],
yAxes: [{
color: ["#123456", "#234567"]
}]
}
},
plugins: [{
beforeDraw: function(chart) {
var ctx = chart.chart.ctx;
var ruleIndex = 0;
var rules = chart.chart.options.backgroundRules;
var yaxis = chart.chart.scales["y-axis-0"];
var xaxis = chart.chart.scales["x-axis-0"];
var partPercentage = 1 / (yaxis.ticksAsNumbers.length - 1);
for (var i = yaxis.ticksAsNumbers.length - 1; i > 0; i--) {
if (yaxis.ticksAsNumbers[i] < rules[ruleIndex].yAxisSegement) {
ctx.fillStyle = rules[ruleIndex].backgroundColor;
ctx.fillRect(xaxis.left, yaxis.top + (i - 1) * (yaxis.height * partPercentage), xaxis.width, yaxis.height * partPercentage);
} else {
ruleIndex++;
i++;
}
}
}
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.js"></script>
<div class="container">
<canvas id="chart"></canvas>
</div>
Let me present a generic approach that works with any such chart aslong as its dataset contains zero or positive values only.
The background colors together with the upper values can simply be defined inside the dataset as follows:
bgColors: [
{ color: 'red', upTo: 40 },
{ color: 'yellow', upTo: 70 },
{ color: 'green', upTo: 100 }
]
Then you could extend an existing line chart (i.e. 'lineDiffBgColors') and overwrite its update function. In there, you would create a linear CanvasGradient and add color stops that correspond to the definitions of bgColors mentioned above. At the end, the linear gradient needs to be assigned to the backgroundColor option of your dataset.
this.chart.data.datasets[0].backgroundColor = gradient;
Please have a look at your enhanced code below.
Chart.defaults.lineDiffBgColors = Chart.defaults.line;
Chart.controllers.lineDiffBgColors = Chart.controllers.line.extend({
update: function(reset) {
var yAxis = this.chart.scales['y-axis-0'];
var bgColors = this.chart.data.datasets[0].bgColors.slice().reverse();
var max = Math.max.apply(null, bgColors.map(o => o.upTo));
var min = yAxis.getValueForPixel(yAxis.bottom);
var yTop = yAxis.getPixelForValue(max);
var gradient = this.chart.chart.ctx.createLinearGradient(0, yTop, 0, yAxis.bottom);
let offset = 0;
bgColors.forEach((bgc, i) => {
gradient.addColorStop(offset, bgc.color);
if (i + 1 == bgColors.length) {
offset = 1;
} else {
offset = (max - bgColors[i + 1].upTo) / (max - min);
}
gradient.addColorStop(offset, bgc.color);
});
this.chart.data.datasets[0].backgroundColor = gradient;
return Chart.controllers.line.prototype.update.apply(this, arguments);
}
});
new Chart('myChart', {
type: 'lineDiffBgColors',
data: {
datasets: [{
label: 'Dataset',
data: [
{ x: 1, y: 10 },
{ x: 2, y: 50 },
{ x: 3, y: 88 },
{ x: 4, y: 5 }
],
bgColors: [
{ color: 'red', upTo: 40 },
{ color: 'yellow', upTo: 70 },
{ color: 'green', upTo: 100 }
]
}]
},
options: {
scales: {
xAxes: [{
type: 'linear'
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>
<canvas id="myChart" height="100"></canvas>
In case you prefer kind of smooth gradient, you could change the bgColors.forEach loop inside the update function as follows.
bgColors.forEach((bgc, i) => {
gradient.addColorStop(offset == 0 ? 0 : offset + 0.05, bgc.color);
if (i + 1 == bgColors.length) {
offset = 1;
} else {
offset = (max - bgColors[i + 1].upTo) / (max - min);
}
gradient.addColorStop(offset == 1 ? 1 : offset - 0.05, bgc.color);
});
Well you can try something (ugly) like this, there is a comment in the snippet where the gradient is defined.
Surely the colors and composition of the gradient can be determined by input properties like you want. Also, one could play with the gradient position or make a radial gradient instead of a linear one.
One last thing, this snippet can really be improved, basically what I did is to identify how the chart is drawn by the library and in which part of it's lifecycle is done, then I copy it into a plugin and replace the solid background color for a canvas linear gradient ;)
To improve the snippet I would start by trying to use the methods defined inside the Chart object (like lineTo() or drawArea()) instead of copying them inside the plugin, then implement the options defined inside the options object to create the linear gradient.
var ctx = document.getElementById("chart").getContext("2d");
var scatterChart = new Chart(ctx, {
type: "line",
data: {
datasets: [{
label: " Dataset",
data: [{
x: 1,
y: 10
},
{
x: 2,
y: 50
},
{
x: 3,
y: 88
},
{
x: 4,
y: 5
}
]
}]
},
options: {
scales: {
xAxes: [{
type: "linear",
position: "bottom"
}]
}
},
plugins: [{
beforeDatasetDraw: function(chart, options) {
var metasets = chart._getSortedVisibleDatasetMetas();
var ctx = chart.ctx;
var meta, i, el, view, points, mapper, color;
var clipArea = (ctx, area) => {
ctx.save();
ctx.beginPath();
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
ctx.clip();
};
var unclipArea = (ctx) => {
ctx.restore();
};
var isDrawable = (point) => {
return point && !point.skip;
}
var lineTo = (ctx, previous, target, flip) => {
var stepped = target.steppedLine;
if (stepped) {
if (stepped === 'middle') {
var midpoint = (previous.x + target.x) / 2.0;
ctx.lineTo(midpoint, flip ? target.y : previous.y);
ctx.lineTo(midpoint, flip ? previous.y : target.y);
} else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) {
ctx.lineTo(previous.x, target.y);
} else {
ctx.lineTo(target.x, previous.y);
}
ctx.lineTo(target.x, target.y);
return;
}
if (!target.tension) {
ctx.lineTo(target.x, target.y);
return;
}
ctx.bezierCurveTo(
flip ? previous.controlPointPreviousX : previous.controlPointNextX,
flip ? previous.controlPointPreviousY : previous.controlPointNextY,
flip ? target.controlPointNextX : target.controlPointPreviousX,
flip ? target.controlPointNextY : target.controlPointPreviousY,
target.x,
target.y);
}
var drawArea = (ctx, curve0, curve1, len0, len1) => {
var i, cx, cy, r;
if (!len0 || !len1) {
return;
}
// building first area curve (normal)
ctx.moveTo(curve0[0].x, curve0[0].y);
for (i = 1; i < len0; ++i) {
lineTo(ctx, curve0[i - 1], curve0[i]);
}
if (curve1[0].angle !== undefined) {
cx = curve1[0].cx;
cy = curve1[0].cy;
r = Math.sqrt(Math.pow(curve1[0].x - cx, 2) + Math.pow(curve1[0].y - cy, 2));
for (i = len1 - 1; i > 0; --i) {
ctx.arc(cx, cy, r, curve1[i].angle, curve1[i - 1].angle, true);
}
return;
}
// joining the two area curves
ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);
// building opposite area curve (reverse)
for (i = len1 - 1; i > 0; --i) {
lineTo(ctx, curve1[i], curve1[i - 1], true);
}
}
var doFill = (ctx, points, mapper, view, color, loop) => {
var count = points.length;
var span = view.spanGaps;
var curve0 = [];
var curve1 = [];
var len0 = 0;
var len1 = 0;
var i, ilen, index, p0, p1, d0, d1, loopOffset;
ctx.beginPath();
for (i = 0, ilen = count; i < ilen; ++i) {
index = i % count;
p0 = points[index]._view;
p1 = mapper(p0, index, view);
d0 = isDrawable(p0);
d1 = isDrawable(p1);
if (loop && loopOffset === undefined && d0) {
loopOffset = i + 1;
ilen = count + loopOffset;
}
if (d0 && d1) {
len0 = curve0.push(p0);
len1 = curve1.push(p1);
} else if (len0 && len1) {
if (!span) {
drawArea(ctx, curve0, curve1, len0, len1);
len0 = len1 = 0;
curve0 = [];
curve1 = [];
} else {
if (d0) {
curve0.push(p0);
}
if (d1) {
curve1.push(p1);
}
}
}
}
drawArea(ctx, curve0, curve1, len0, len1);
ctx.closePath();
ctx.fillStyle = color;
ctx.fill();
}
for (i = metasets.length - 1; i >= 0; --i) {
meta = metasets[i].$filler;
if (!meta || !meta.visible) {
continue;
}
el = meta.el;
view = el._view;
points = el._children || [];
mapper = meta.mapper;
// NOTE: HERE IS WHERE THE GRADIENT IS DEFINED. ONE COULD PROBABLY CREATE THE GRADIENT BASED ON INPUT DATA INSIDE THE OPTIONS OBJECT.
color = ctx.createLinearGradient(chart.width / 2, chart.height, chart.width / 2, 0);
color.addColorStop(0, 'red');
color.addColorStop(0.2, 'red');
color.addColorStop(0.4, 'yellow');
color.addColorStop(0.6, 'yellow');
color.addColorStop(0.8, 'green');
color.addColorStop(1, 'green');
if (mapper && color && points.length) {
clipArea(ctx, chart.chartArea);
doFill(ctx, points, mapper, view, color, el._loop);
unclipArea(ctx);
}
}
}
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.js"></script>
<div class="container">
<canvas id="chart"></canvas>
</div>
I have a high chart on my web page which is a line chart. It has a functionality to zoom and I capture the zoom event using chart.events.selection. That all is working fine.
But I want to continuously capture the selection event (i.e. basically click and drag event) to show a tooltip in the beginning and end of the selection to show the time user has selected. Couldn't find in high charts documentation. Any help would be appreciated.
Here is my current code to capture selection event:
$(obj.id).highcharts({
chart: {
type: 'areaspline',
backgroundColor:"rgba(0,0,0,0)",
zoomType:"x",
events: {
selection: function(event){
if(!event.xAxis)
return;
.....
Updated:
Updated example in which labels following selection marker: http://jsfiddle.net/pq0wn0xx/2/
I do not think there is a drag event (not for points), but you can wrap drag pointer's method.
Highcharts.wrap(Highcharts.Pointer.prototype, 'drag', function (p, e) {
p.call(this, e);
var H = Highcharts,
chart = this.chart,
selectionMarker = this.selectionMarker,
bBox,
xAxis,
labelLeft,
labelRight,
labelY,
attr,
css,
timerLeft,
timerRight;
if (selectionMarker) {
if (!chart.customLabels) {
chart.customLabels = [];
}
bBox = selectionMarker.getBBox();
xAxis = chart.xAxis[0];
labelLeft = chart.customLabels[0];
labelRight = chart.customLabels[1];
labelY = chart.plotTop + 10;
if (!labelLeft || !labelRight) {
attr = {
fill: Highcharts.getOptions().colors[0],
padding: 10,
r: 5,
zIndex: 8
};
css = {
color: '#FFFFFF'
};
labelLeft = chart.renderer.label('', 0, 0).attr(attr).css(css).add();
labelRight = chart.renderer.label('', 0, 0).attr(attr).css(css).add();
chart.customLabels.push(labelLeft, labelRight);
}
clearTimeout(timerLeft);
clearTimeout(timerRight);
labelLeft.attr({
x: bBox.x - labelLeft.getBBox().width,
y: labelY,
text: 'min: ' + H.numberFormat(xAxis.toValue(bBox.x), 2),
opacity: 1
});
labelRight.attr({
x: bBox.x + bBox.width,
y: labelY,
text: 'max: ' + H.numberFormat(xAxis.toValue(bBox.x + bBox.width), 2),
opacity: 1
});
timerLeft = setTimeout(function () {
labelLeft.fadeOut();
}, 3000);
timerRight = setTimeout(function () {
labelRight.fadeOut();
}, 3000);
}
});
Old answer:
The example from the official API
can be extended to what you need.
The code and the example on jsfiddle are below:
function positionLabels(e, chart) {
if (!chart.customLabels) {
chart.customLabels = [];
}
var labelLeft,
labelRight,
attr,
css,
xAxis,
xMin,
xMax,
yAxis,
yMin,
yMax,
yMiddle,
timerLeft,
timerRight;
if (!e.resetSelection) {
labelLeft = chart.customLabels[0];
labelRight = chart.customLabels[1];
if (!labelLeft || !labelRight) {
attr = {
fill: Highcharts.getOptions().colors[0],
padding: 10,
r: 5,
zIndex: 8
};
css = {
color: '#FFFFFF'
};
labelLeft = chart.renderer.label('', 0, 0).attr(attr).css(css).add();
labelRight = chart.renderer.label('', 0, 0).attr(attr).css(css).add();
chart.customLabels.push(labelLeft, labelRight);
}
clearTimeout(timerLeft);
clearTimeout(timerRight);
xAxis = e.xAxis[0].axis;
xMin = e.xAxis[0].min;
xMax = e.xAxis[0].max;
yAxis = chart.yAxis[0];
yMin = yAxis.min;
yMax = yAxis.max;
yMiddle = (yMax - yMin) * 0.95;
labelLeft.attr({
x: xAxis.toPixels(xMin) - labelLeft.getBBox().width,
y: yAxis.toPixels(yMiddle),
text: 'min: ' + Highcharts.numberFormat(xMin, 2),
opacity: 1
});
labelRight.attr({
x: xAxis.toPixels(xMax),
y: yAxis.toPixels(yMiddle),
text: 'max: ' + Highcharts.numberFormat(xMax, 2),
opacity: 1
});
timerLeft = setTimeout(function () {
labelLeft.fadeOut();
}, 2000);
timerRight = setTimeout(function () {
labelRight.fadeOut();
}, 2000);
}
}
example: http://jsfiddle.net/pq0wn0xx/
I am trying to insert a x3d object via ajax callback but the object doesn't appear. I copied the source code of the page then placed it on a new page then the x3d object showed. Am I missing something here? Is there a work around for this? thanks.
html:
<div id="divPlot" style="border: 1px solid black"></div>
<button onclick="add_x3d()">Click</button>
<script>
d3.select('html').style('height','100%').style('width','100%');
d3.select('body').style('height','100%').style('width','100%');
d3.select('#divPlot').style('width', "450px").style('height', "450px");
function add_x3d() {
scatterPlot3d(d3.select('#divPlot'));
}
</script>
javascript:
function scatterPlot3d(parent){
var rows = [
{"SITE":"1","SIGNAME":"A","X":10,"Y":10,"Z":111},
{"SITE":"1","SIGNAME":"B","X":200,"Y":10,"Z":222},
{"SITE":"2","SIGNAME":"A","X":10,"Y":40,"Z":333},
{"SITE":"2","SIGNAME":"B","X":200,"Y":40,"Z":444},
{"SITE":"3","SIGNAME":"A","X":10,"Y":70,"Z":555},
{"SITE":"3","SIGNAME":"B","X":200,"Y":70,"Z":666},
{"SITE":"4","SIGNAME":"A","X":10,"Y":100,"Z":777},
{"SITE":"4","SIGNAME":"B","X":200,"Y":100,"Z":888}
];
var x3d = parent
.append("x3d")
.style("width", parseInt(parent.style("width")) + "px")
.style("height", parseInt(parent.style("height")) + "px")
.style("border", "none");
var scene = x3d.append("scene");
scene.append("orthoviewpoint")
.attr("centerOfRotation", [5, 5, 5])
.attr("fieldOfView", [-5, -5, 15, 15])
.attr("orientation", [-0.5, 1, 0.2, 1.12 * Math.PI / 4])
.attr("position", [8, 4, 15]);
// Used to make 2d elements visible
function makeSolid(selection, color) {
selection.append("appearance")
.append("material")
.attr("diffuseColor", color || "black");
return selection;
}
function constVecWithAxisValue(otherValue, axisValue, axisIndex) {
var result = [otherValue, otherValue, otherValue];
result[axisIndex] = axisValue;
return result;
}
var XAxisMin = d3.min(rows, function(d){return d.X;});
var XAxisMax = d3.max(rows, function(d){return d.X;});
var XAxisDel = XAxisMax-XAxisMin;
var YAxisMin = d3.min(rows, function(d){return d.Y;});
var YAxisMax = d3.max(rows, function(d){return d.Y;});
var YAxisDel = YAxisMax-YAxisMin;
var ZAxisMin = d3.min(rows, function(d){return d.Z;});
var ZAxisMax = d3.max(rows, function(d){return d.Z;});
var ZAxisDel = ZAxisMax-ZAxisMin;
function AxisMin(axisIndex) {
return [XAxisMin, ZAxisMin, YAxisMin][axisIndex];
}
function AxisMax(axisIndex) {
return [XAxisMax, ZAxisMax, YAxisMax][axisIndex];
}
function AxisDel(axisIndex) {
return [XAxisDel, ZAxisDel, YAxisDel][axisIndex];
}
function axisName(name, axisIndex) {
return AxisKeys[axisIndex] + name;
}
function get2DVal(){
if (XAxisDel >= YAxisDel){
return XAxisDel;
} else {
return YAxisDel;
}
}
function ConvAxisRange(inputVal, axisIndex) {
var val;
if (axisIndex === 0 || axisIndex === 2) {
val = d3.scale.linear()
.domain([0, delta2D])
.range(AxisRange);
} else {
val = d3.scale.linear()
.domain([0, ZAxisDel])
.range(AxisRange);
}
return val(inputVal);
}
function ConvAxisRange2D(inputVal) {
var val = d3.scale.linear()
.domain([0, delta2D])
.range(AxisRange);
return val(inputVal);
}
var AxisKeys = ["X", "HEIGHT", "Y"];
var AxisRange = [0, 10];
var scales = [];
var AxisLen;
var duration = 300;
var delta2D = get2DVal();
var ArrayOfColors = ["#0000FF", "#00FFFF", "#00FF00", "#FFFF00", "#FF0000"];
var colorScale = d3.scale.linear()
.domain([0, ZAxisDel*0.25, ZAxisDel*0.50, ZAxisDel*0.75, ZAxisDel])
.range(ArrayOfColors);
function initializeAxis(axisIndex) {
var key = AxisKeys[axisIndex];
drawAxis(axisIndex, key, duration);
var scaleDel = AxisDel(axisIndex);
var rotation = [[0, 0, 0, 0], [0, 0, 1, Math.PI / 2], [0, 1, 0, -Math.PI / 2]];
var newAxisLine = scene.append("transform")
.attr("class", axisName("Axis", axisIndex))
.attr("rotation", (rotation[axisIndex]))
.append("shape");
newAxisLine
.append("appearance")
.append("material")
.attr("emissiveColor", "lightgray");
newAxisLine
.append("polyline2d")
// Line drawn along y axis does not render in Firefox, so draw one
// along the x axis instead and rotate it (above).
.attr("lineSegments", "[" + ConvAxisRange(scaleDel, axisIndex) + " 0, 0 0]");
// axis labels
var newAxisLabel = scene.append("transform")
.attr("class", axisName("AxisLabel", axisIndex))
.attr("translation", constVecWithAxisValue(0, ConvAxisRange(scaleDel*1.15, axisIndex), axisIndex));
var newAxisLabelShape = newAxisLabel
.append("billboard")
.attr("axisOfRotation", "0 0 0") // face viewer
.append("shape")
.call(makeSolid);
var labelFontSize = 0.6;
newAxisLabelShape
.append("text")
.attr("class", axisName("AxisLabelText", axisIndex))
.attr("solid", "true")
.attr("string", key)
.append("fontstyle")
.attr("size", labelFontSize)
.attr("family", "SANS")
.attr("justify", "END MIDDLE");
}
// Assign key to axis, creating or updating its ticks, grid lines, and labels.
function drawAxis(axisIndex, key, duration) {
var scale;
if (axisIndex === 0 || axisIndex === 2) {
scale = d3.scale.linear()
.domain([AxisMin(axisIndex), AxisMax(axisIndex)]) // demo data range
.range([0, ConvAxisRange2D(AxisDel(axisIndex))]);
} else {
scale = d3.scale.linear()
.domain([AxisMin(axisIndex), AxisMax(axisIndex)]) // demo data range
.range(AxisRange);
}
scales[axisIndex] = scale;
var numTicks = 5;
var tickSize = 0.1;
var tickFontSize = 0.5;
// ticks along each axis
var ticks = scene.selectAll("." + axisName("Tick", axisIndex))
.data(scale.ticks(numTicks));
var newTicks = ticks.enter()
.append("transform")
.attr("class", axisName("Tick", axisIndex));
newTicks.append("shape").call(makeSolid)
.append("box")
.attr("size", tickSize + " " + tickSize + " " + tickSize);
// enter + update
ticks.transition().duration(duration)
.attr("translation", function(tick) {
return constVecWithAxisValue(0, scale(tick), axisIndex);
});
ticks.exit().remove();
// tick labels
var tickLabels = ticks.selectAll("billboard shape text")
.data(function(d) {
return [d];
});
var newTickLabels = tickLabels.enter()
.append("billboard")
.attr("axisOfRotation", "0 0 0")
.append("shape")
.call(makeSolid);
newTickLabels.append("text")
.attr("string", scale.tickFormat(10))
.attr("solid", "true")
.append("fontstyle")
.attr("size", tickFontSize)
.attr("family", "SANS")
.attr("justify", "END MIDDLE");
tickLabels // enter + update
.attr("string", scale.tickFormat(10));
tickLabels.exit().remove();
}
function plotData() {
if (!rows) {
console.log("no rows to plot.");
return;
}
var x = scales[0], z = scales[1], y = scales[2];
var sphereRadius = 0.2;
// Draw a sphere at each x,y,z coordinate.
var datapoints = scene.selectAll(".datapoint").data(rows);
datapoints.exit().remove();
var newDatapoints = datapoints.enter()
.append("transform")
.attr("class", "datapoint")
.attr("scale", [sphereRadius, sphereRadius, sphereRadius])
.append("shape");
newDatapoints
.append("appearance")
.append("material");
newDatapoints
.append("sphere");
// Does not work on Chrome; use transform instead
//.attr("radius", sphereRadius)
datapoints.selectAll("shape appearance material")
.attr("diffuseColor", function(row){
return colorScale(row.Z-ZAxisMin);
});
datapoints.attr("translation", function(row) {
return x(row.X) + " " + z(row.Z) + " " + y(row.Y);
});
}
function initializePlot() {
initializeAxis(0);
initializeAxis(1);
initializeAxis(2);
}
initializePlot();
}
You cannot add the whole x3d element and a scene dynamically per se since x3dom is initialized with an window.onload event. This should be part of your HTML document beforehand. Then you can add the elements (views, shapes etc) to the scene.
But I heard sometime ago something about a reload function (https://github.com/x3dom/x3dom/blob/1.7.1/src/Main.js#L327) in the mailing list, sadly this is not well documented:
/** Initializes an <x3d> root element that was added after document load. */
x3dom.reload = function() {
onload();
};
This should be doing what you want.
I am using chartJS library - http://www.chartjs.org/
If the text label is too big, chartJs displays text at 45 degrees
Please tell me how can I change the text rotation to 30 degrees for instance.
Please see the following :
var data = { labels:['Large Text Very Large','Text1','Text2'],
datasets:[ { label:'DS1',
fillColor:'rgba(228,218,86,0.5)',
strokeColor:'rgba(228,218,86,0.8)',
highlightFill:'rgba(228,218,86,0.75)',
highlightStroke: 'rgba(228,218,86,1)', data:[0,1,0]
}
]
}
var options = {
animation: false
};
//Get the context of the canvas element we want to select
var c = $('#myChart');
var ct = c.get(0).getContext('2d');
var ctx = document.getElementById("myChart").getContext("2d");
/*************************************************************************/
myNewChart = new Chart(ct).Bar(data, options);
Also on jsfiddle:
http://jsfiddle.net/cvL9fk2t/
Thanks !
If you are using chart.js 2.x, just set maxRotation: 90 and minRotation: 90 in ticks options. It works for me!
And if you want to all x-labels, you may want to set autoSkip: false.
The following is an example.
var myChart = new Chart(ctx, {
type: 'bar',
data: chartData,
options: {
scales: {
xAxes: [{
ticks: {
autoSkip: false,
maxRotation: 90,
minRotation: 90
}
}]
}
}
});
You would need to extend the scale class and override the calculateXLabelRotation method to use a user inputted rotation rather than trying to work it out it's self. If you do this you would then need to extend the bar or line chart and override the init method to make use of this scale class. (or you could make these changes directly to the scale, bar and line classes and then no need to override).
so first extend scale class and make it use a user defined option
var helpers = Chart.helpers;
Chart.MyScale = Chart.Scale.extend({
calculateXLabelRotation: function() {
//Get the width of each grid by calculating the difference
//between x offsets between 0 and 1.
this.ctx.font = this.font;
var firstWidth = this.ctx.measureText(this.xLabels[0]).width,
lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width,
firstRotated,
lastRotated;
this.xScalePaddingRight = lastWidth / 2 + 3;
this.xScalePaddingLeft = (firstWidth / 2 > this.yLabelWidth + 10) ? firstWidth / 2 : this.yLabelWidth + 10;
this.xLabelRotation = 0;
if (this.display) {
var originalLabelWidth = helpers.longestText(this.ctx, this.font, this.xLabels),
cosRotation,
firstRotatedWidth;
this.xLabelWidth = originalLabelWidth;
//Allow 3 pixels x2 padding either side for label readability
var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6;
//check if option is set if so use that
if (this.overrideRotation) {
// do the same as before but manualy set the rotation rather than looping
this.xLabelRotation = this.overrideRotation;
cosRotation = Math.cos(helpers.radians(this.xLabelRotation));
// We're right aligning the text now.
if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8) {
this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
}
this.xScalePaddingRight = this.fontSize / 2;
this.xLabelWidth = cosRotation * originalLabelWidth;
} else {
//Max label rotate should be 90 - also act as a loop counter
while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)) {
cosRotation = Math.cos(helpers.radians(this.xLabelRotation));
firstRotated = cosRotation * firstWidth;
lastRotated = cosRotation * lastWidth;
// We're right aligning the text now.
if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8) {
this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
}
this.xScalePaddingRight = this.fontSize / 2;
this.xLabelRotation++;
this.xLabelWidth = cosRotation * originalLabelWidth;
}
}
if (this.xLabelRotation > 0) {
this.endPoint -= Math.sin(helpers.radians(this.xLabelRotation)) * originalLabelWidth + 3;
}
} else {
this.xLabelWidth = 0;
this.xScalePaddingRight = this.padding;
this.xScalePaddingLeft = this.padding;
}
},
});
then in the extend the bar class to create a new graph type and override the init method to use the new
Chart.types.Bar.extend({
name: "MyBar",
initialize: function(data) {
//Expose options as a scope variable here so we can access it in the ScaleClass
var options = this.options;
this.ScaleClass = Chart.MyScale.extend({
overrideRotation: options.overrideRotation,
offsetGridLines: true,
calculateBarX: function(datasetCount, datasetIndex, barIndex) {
//Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
var xWidth = this.calculateBaseWidth(),
xAbsolute = this.calculateX(barIndex) - (xWidth / 2),
barWidth = this.calculateBarWidth(datasetCount);
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth / 2;
},
calculateBaseWidth: function() {
return (this.calculateX(1) - this.calculateX(0)) - (2 * options.barValueSpacing);
},
calculateBarWidth: function(datasetCount) {
//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);
return (baseWidth / datasetCount);
}
});
this.datasets = [];
//Set up tooltip events on the chart
if (this.options.showTooltips) {
helpers.bindEvents(this, this.options.tooltipEvents, function(evt) {
var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
this.eachBars(function(bar) {
bar.restore(['fillColor', 'strokeColor']);
});
helpers.each(activeBars, function(activeBar) {
activeBar.fillColor = activeBar.highlightFill;
activeBar.strokeColor = activeBar.highlightStroke;
});
this.showTooltip(activeBars);
});
}
//Declare the extension of the default point, to cater for the options passed in to the constructor
this.BarClass = Chart.Rectangle.extend({
strokeWidth: this.options.barStrokeWidth,
showStroke: this.options.barShowStroke,
ctx: this.chart.ctx
});
//Iterate through each of the datasets, and build this into a property of the chart
helpers.each(data.datasets, function(dataset, datasetIndex) {
var datasetObject = {
label: dataset.label || null,
fillColor: dataset.fillColor,
strokeColor: dataset.strokeColor,
bars: []
};
this.datasets.push(datasetObject);
helpers.each(dataset.data, function(dataPoint, index) {
//Add a new point for each piece of data, passing any required data to draw.
datasetObject.bars.push(new this.BarClass({
value: dataPoint,
label: data.labels[index],
datasetLabel: dataset.label,
strokeColor: dataset.strokeColor,
fillColor: dataset.fillColor,
highlightFill: dataset.highlightFill || dataset.fillColor,
highlightStroke: dataset.highlightStroke || dataset.strokeColor
}));
}, this);
}, this);
this.buildScale(data.labels);
this.BarClass.prototype.base = this.scale.endPoint;
this.eachBars(function(bar, index, datasetIndex) {
helpers.extend(bar, {
width: this.scale.calculateBarWidth(this.datasets.length),
x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
y: this.scale.endPoint
});
bar.save();
}, this);
this.render();
},
});
now you can declare a chart using this chart type and pass in the option overrideRotation
here is a fiddle example http://jsfiddle.net/leighking2/ye3usuhu/
and a snippet
var helpers = Chart.helpers;
Chart.MyScale = Chart.Scale.extend({
calculateXLabelRotation: function() {
//Get the width of each grid by calculating the difference
//between x offsets between 0 and 1.
this.ctx.font = this.font;
var firstWidth = this.ctx.measureText(this.xLabels[0]).width,
lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width,
firstRotated,
lastRotated;
this.xScalePaddingRight = lastWidth / 2 + 3;
this.xScalePaddingLeft = (firstWidth / 2 > this.yLabelWidth + 10) ? firstWidth / 2 : this.yLabelWidth + 10;
this.xLabelRotation = 0;
if (this.display) {
var originalLabelWidth = helpers.longestText(this.ctx, this.font, this.xLabels),
cosRotation,
firstRotatedWidth;
this.xLabelWidth = originalLabelWidth;
//Allow 3 pixels x2 padding either side for label readability
var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6;
if (this.overrideRotation) {
this.xLabelRotation = this.overrideRotation;
cosRotation = Math.cos(helpers.radians(this.xLabelRotation));
// We're right aligning the text now.
if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8) {
this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
}
this.xScalePaddingRight = this.fontSize / 2;
this.xLabelWidth = cosRotation * originalLabelWidth;
} else {
//Max label rotate should be 90 - also act as a loop counter
while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)) {
cosRotation = Math.cos(helpers.radians(this.xLabelRotation));
firstRotated = cosRotation * firstWidth;
lastRotated = cosRotation * lastWidth;
// We're right aligning the text now.
if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8) {
this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
}
this.xScalePaddingRight = this.fontSize / 2;
this.xLabelRotation++;
this.xLabelWidth = cosRotation * originalLabelWidth;
}
}
if (this.xLabelRotation > 0) {
this.endPoint -= Math.sin(helpers.radians(this.xLabelRotation)) * originalLabelWidth + 3;
}
} else {
this.xLabelWidth = 0;
this.xScalePaddingRight = this.padding;
this.xScalePaddingLeft = this.padding;
}
},
});
Chart.types.Bar.extend({
name: "MyBar",
initialize: function(data) {
//Expose options as a scope variable here so we can access it in the ScaleClass
var options = this.options;
this.ScaleClass = Chart.MyScale.extend({
overrideRotation: options.overrideRotation,
offsetGridLines: true,
calculateBarX: function(datasetCount, datasetIndex, barIndex) {
//Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
var xWidth = this.calculateBaseWidth(),
xAbsolute = this.calculateX(barIndex) - (xWidth / 2),
barWidth = this.calculateBarWidth(datasetCount);
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth / 2;
},
calculateBaseWidth: function() {
return (this.calculateX(1) - this.calculateX(0)) - (2 * options.barValueSpacing);
},
calculateBarWidth: function(datasetCount) {
//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);
return (baseWidth / datasetCount);
}
});
this.datasets = [];
//Set up tooltip events on the chart
if (this.options.showTooltips) {
helpers.bindEvents(this, this.options.tooltipEvents, function(evt) {
var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
this.eachBars(function(bar) {
bar.restore(['fillColor', 'strokeColor']);
});
helpers.each(activeBars, function(activeBar) {
activeBar.fillColor = activeBar.highlightFill;
activeBar.strokeColor = activeBar.highlightStroke;
});
this.showTooltip(activeBars);
});
}
//Declare the extension of the default point, to cater for the options passed in to the constructor
this.BarClass = Chart.Rectangle.extend({
strokeWidth: this.options.barStrokeWidth,
showStroke: this.options.barShowStroke,
ctx: this.chart.ctx
});
//Iterate through each of the datasets, and build this into a property of the chart
helpers.each(data.datasets, function(dataset, datasetIndex) {
var datasetObject = {
label: dataset.label || null,
fillColor: dataset.fillColor,
strokeColor: dataset.strokeColor,
bars: []
};
this.datasets.push(datasetObject);
helpers.each(dataset.data, function(dataPoint, index) {
//Add a new point for each piece of data, passing any required data to draw.
datasetObject.bars.push(new this.BarClass({
value: dataPoint,
label: data.labels[index],
datasetLabel: dataset.label,
strokeColor: dataset.strokeColor,
fillColor: dataset.fillColor,
highlightFill: dataset.highlightFill || dataset.fillColor,
highlightStroke: dataset.highlightStroke || dataset.strokeColor
}));
}, this);
}, this);
this.buildScale(data.labels);
this.BarClass.prototype.base = this.scale.endPoint;
this.eachBars(function(bar, index, datasetIndex) {
helpers.extend(bar, {
width: this.scale.calculateBarWidth(this.datasets.length),
x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
y: this.scale.endPoint
});
bar.save();
}, this);
this.render();
},
});
var randomScalingFactor = function() {
return Math.round(Math.random() * 100)
};
var barChartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.75)",
highlightStroke: "rgba(220,220,220,1)",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
}, {
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,0.8)",
highlightFill: "rgba(151,187,205,0.75)",
highlightStroke: "rgba(151,187,205,1)",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
}, {
fillColor: "rgba(15,18,20,0.5)",
strokeColor: "rgba(15,18,20,0.8)",
highlightFill: "rgba(15,18,20,0.75)",
highlightStroke: "rgba(15,18,20,1)",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
}]
}
window.onload = function() {
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx).MyBar(barChartData, {
overrideRotation: 30
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.1/Chart.js"></script>
<canvas id="canvas" height="150" width="300"></canvas>
Note that for chart.js 3.x the way of specifying the axis scale options has changed: see https://www.chartjs.org/docs/master/getting-started/v3-migration.html#scales
Consequently in the above answer for 2.x you need to remove the square brackets like this:
var myChart = new Chart(ctx, {
type: 'bar',
data: chartData,
options: {
scales: {
xAxes: {
ticks: {
autoSkip: false,
maxRotation: 90,
minRotation: 90
}
}
}
}
});
This question already has answers here:
How to set ChartJS Y axis title?
(7 answers)
Closed 7 years ago.
Hello everybody,
I'm doing some bar charts with chartjs...everything goes well but i can't find how to name my axes the clean way, i mean without using css trick like position absolute etc..
for example, this is one of my chart :
function buildResultChart(suffix, vous, moyenne) {
var pixelRatio = window.devicePixelRatio || 1;
var $chart = $('#result-chart-' + suffix);
var width = $chart.parents('.inner').width() / pixelRatio;
var containerWidth = $('.container').width();
if (containerWidth < 320) {
containerWidth = 320;
}
if (containerWidth > 600) {
containerWidth = 600;
}
var width = containerWidth - 40;
$chart.attr('width', width);
var goodColor = 'rgba(200,245,0,1)';
var badColor = 'rgba(255,254,134,1)';
var color = goodColor;
if (moyenne < vous) {
color = badColor;
}
var ctx = document.getElementById("result-chart-" + suffix).getContext("2d");
var data = {
labels: ['Votre salon'],
datasets: [
{
fillColor: "rgba(220,220,220,1)",
fillColorAlt: color,
strokeColor: "rgba(220,220,220,1)",
data: [vous]
},
]
};
var max = Math.round(Math.max(vous, moyenne));
//highest 100 next
var len = max.toString().length;
var first = max.toString().substring(0, 1);
if (first === '9') {
len++;
first = '1';
}
else {
first = parseInt(first) + 1;
}
while (--len) {
first += '0';
}
var max = first;
var steps = 10;
var stepWidth = Math.round(max / steps);
var c = new Chart(ctx).Bar(data, {
align: 'h',
scaleShowLabels:true,
scaleFontColor: '#fff',
scaleLineColor: '#fff',
scaleShowGridLines: false,
barShowStroke: false,
animationSteps: 80,
scaleOverride: true,
scaleStartValue: 0,
scaleSteps: steps,
scaleStepWidth: stepWidth
});
}
Maybe someone could help me?
Thanks a lot!
Hello,
For the ones who have the same problem, a new version of chartJS is in dev mode, you can see it there newChartJS.
This version seems to let you name your axis but doesn't work 100% the same way than the older so, be careful :)