How to change x-Axes label position in Chart.js - javascript

Is it possible to change the label position, like padding or margin in CSS?
I would like to have the labels above the x-Axes.

You can change the xAxes label positions with the following steps:
Add .getContext("2d") to the call that gets the ctx canvas object:
var ctx = document.getElementById("gescanntePackzettelChart").getContext("2d");
Hide the current xAxes ticks:
xAxes: [{
ticks: {
display: false
}
}
Add an animation to the options config, and use the canvas fillText() method to create new labels:
animation: {
duration: 1,
onComplete: function() {
var controller = this.chart.controller;
var chart = controller.chart;
var xAxis = controller.scales['x-axis-0'];
var numTicks = xAxis.ticks.length;
var xOffsetStart = xAxis.width / numTicks;
var halfBarWidth = (xAxis.width / (numTicks * 2));
xAxis.ticks.forEach(function(value, index) {
var xOffset = (xOffsetStart * index) + halfBarWidth;
var yOffset = chart.height - 20;
ctx.fillText(value, xOffset, yOffset);
});
}
}
For xOffset, to figure out the correct position, take the xAxis width and divide it by the number of tick labels. This will tell you how wide each bar is. Then, multiply that number by the current index to position the label in front of the correct bar. Finally, add add the width of half a bar to center the labels in the bar.
For the yOffset, start with the chart's height and subtract however much you want to move the labels up.
Working JSFiddle: https://jsfiddle.net/m5tnkr4n/124

Building on #Tot-Zam's response, it's possible to use the ChartJs scale object's methods to define the context pixel coordinates easier. You also don't need to define a ctx object - it is already accessible using the this.chart object.
animation: {
duration: 0,
onComplete: function() {
let chart = this.chart;
let controller = chart.controller;
let axis = controller.scales['x-axis-0'];
let yOffset = chart.height - 5;
axis.ticks.forEach(function(value, index) {
let xOffset = axis.getPixelForValue(value);
chart.ctx.fillText(value, xOffset, yOffset);
})
}
}
Working repl: https://repl.it/#lunarplasma/ChartJs-custom-axis-labels

Related

How do I get rid of the excess white space around pie chart? Using amcharts 4 libs

I have been trying to spread the pie chart I have to cover most of the 'card' I on the page, but no matter how much margin I cut, it either start disappearing behind another border within the card. I noticed that amcharts lib creates a bunch of layers that the developer doesn't have much control over. Anyway, this is what I'm talking about:
Here is the generated HTML code snippet:
Here is my javascript:
am4core.ready(function () {
//Themes
var chartType = am4charts.PieChart3D;
var seriesType = new am4charts.PieSeries3D();
//Create Chart and Series
var chart = createChart(thisWidget.id, chartType);
var pieSeries = chart.series.push(seriesType); // 3D Pie Chart
//Set properties
chart.hiddenState.properties.opacity = 0; // 3D Pie Chart: this creates initial fade-in
pieSeries.slices.template.cornerRadius = 6; //Pie Chart with varying Radius + 3D
pieSeries.colors.step = 3; //Pie Chart with varying Radius + 3D
pieSeries.angle = 45;
//color
if (colorTheme) {
pieSeries.colors.list = getAmchartCustomTheme(colorTheme);
}
//data types
pieSeries.dataFields.value = "count";
pieSeries.dataFields.category = "tag";
chart.paddingTop = 0;
chart.marginTop = 0;
// Put a thick white border around each Slice
pieSeries.slices.template.stroke = am4core.color("#fff");
pieSeries.slices.template
// change the cursor on hover to make it apparent the object can be interacted with
.cursorOverStyle = [
{
"property": "cursor",
"value": "pointer"
}
];
//Make the slice move on hover
var slice = pieSeries.slices.template;
slice.states.getKey("active").properties.shiftRadius = 0;
slice.states.getKey("hover").properties.scale = 1;
slice.states.getKey("hover").properties.shiftRadius = 0.2;
//increase size of Chart
chart.svgContainer.htmlElement.style.height = targetHeight;
chart.svgContainer.autoresize = true;
//disable Ticks and labels to save space
pieSeries.labels.template.disabled = true;
//registering events
pieSeries.slices.template.events.on("hit", function (ev) {
var category = ev.target.slice._dataItem.properties.category;
addInput(category);
});
pieSeries.alignLabels = false;
// Create a base filter effect (as if it's not there) for the hover to return to
var shadow = pieSeries.slices.template.filters.push(new am4core.DropShadowFilter);
shadow.opacity = 0;
// Create hover state
var hoverState = pieSeries.slices.template.states.getKey("hover"); // normally we have to create the hover state, in this case it already exists
// Slightly shift the shadow and make it more prominent on hover
var hoverShadow = hoverState.filters.push(new am4core.DropShadowFilter);
hoverShadow.opacity = 0.7;
hoverShadow.blur = 5;
//Add Data
chart.data = displayItems;
})
strokeOpacity = 0 or strokeWidth = 0 should do the trick.

vis.js 3D bar graph: Set bar color based on z-coordinate value

I am trying to use Graph3d from vis.js to create a bar-color where I can control the bar color based on the z-coordinate value.
I have written a sample referring the examples and here the bar color depends on the z-coordinate or height of the bar. viz.js assigns the bar colors relatively, red for low values to blue for high values.
But the problem is if all data points have similar values, the bar color will be close to blue even if the actual values are very low. I would like the low values to be always red and high levels always green/blue. Is there a way of achieving that in viz.js?
var data = null;
var graph = null;
/**
* Returns a random number between min (inclusive) and max (exclusive)
*/
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
// Called when the Visualization API is loaded.
function drawVisualization() {
var style = 'bar-color';
// Create and populate a data table.
data = new vis.DataSet();
//Poppulating the data
for (var x = 1; x <= 10; x++) {
for (var y = 1; y <= 5; y++) {
var z = getRandomArbitrary(1,5);
data.add({x:x, y:y, z:z, style:5-z});
}
}
// specify options
var options = {
width: '700px',
height: '700px',
style: style,
showPerspective: true,
showGrid: true,
showShadow: false,
verticalRatio: 0.5,
zMin: 0,
zMax: 5,
xStep: 1,
xCenter: '50%',
yCenter: '30%',
// Option tooltip can be true, false, or a function returning a string with HTML contents
//tooltip: true,
tooltip: function (point) {
// parameter point contains properties x, y, z
return 'value: <b>' + point.z + '</b>';
},
keepAspectRatio: true,
verticalRatio: 0.5
};
var camera = graph ? graph.getCameraPosition() : null;
// create our graph
var container = document.getElementById('mygraph');
graph = new vis.Graph3d(container, data, options);
if (camera) graph.setCameraPosition(camera); // restore camera position
var pos = {horizontal: 1.0, vertical: 0.5, distance: 2};
graph.setCameraPosition(pos);
}
drawVisualization();
html, body {font: 10pt arial;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
}
#mygraph {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/vis/3.6.4/vis.min.js">
</script>
<body>
<div id="mygraph"></div>
</body>
Here's the jsfiddle link.
I am beginner in vis.js and couldn't find a way of achieving this even after spending hours on their documentation. If there is any other open source graphing solution which can do this rendering a similar looking graph, I would love to know that.
The color depends on the scale of the z-axis. If you manually set the axis range from 0-100 while all your data has values in the range 90-100, all data points will be red, and the graph looks almost flat. If you let the scale be determined automatically, it will just fit the data and scale the colors from min value (90) to max value (100).
There is no official feature to provide your own color scale function, but what you could do is replace the (internally used) graph3d._hsv2rgb(h, s, v) method with your own color scale function.

Add Link to X-Label Chart.js

I'm looking to be able to link the x-labels in a Chart.js bar chart. I've searched pretty thoroughly, and ended up trying to come up with my own solution: because the labels correspond to the bars directly above them and Chart.js has a built in getBarsAtEvent(evt) method, I tried creating an event if the user didn't click on a chart - this new event had pageX and pageY that was directly above the initial click such that if the user had clicked on a label, the new event would simulate a click on the bar graph.
However, calling getBarsAtEvent(createdClickEvent) repeatedly gives me a Uncaught TypeError ("Cannot read property 'getBoundingClientRect' of null"), which must mean that the getBarsAtEvent method, when called on my simulated click, isn't actually returning anything.
Any suggestions or alternate approaches would be greatly appreciated, thanks in advance.
An alternative approach would be to determine the point where the user is actually clicked and based on that calculate which label was clicked. For that you will need some information about the chart created and have to do some calculations.
Below is a way of doing that, and here is a Fiddle with this code/approach. Hope it helps.
$("#canvas").click(
function(evt){
var ctx = document.getElementById("canvas").getContext("2d");
// from the endPoint we get the end of the bars area
var base = myBar.scale.endPoint;
var height = myBar.chart.height;
var width = myBar.chart.width;
// only call if event is under the xAxis
if(evt.pageY > base){
// how many xLabels we have
var count = myBar.scale.valuesCount;
var padding_left = myBar.scale.xScalePaddingLeft;
var padding_right = myBar.scale.xScalePaddingRight;
// calculate width for each label
var xwidth = (width-padding_left-padding_right)/count;
// determine what label were clicked on AND PUT IT INTO bar_index
var bar_index = (evt.offsetX - padding_left) / xwidth;
// don't call for padding areas
if(bar_index > 0 & bar_index < count){
bar_index = parseInt(bar_index);
// either get label from barChartData
console.log("barChartData:" + barChartData.labels[bar_index]);
// or from current data
var ret = [];
for (var i = 0; i < myBar.datasets[0].bars.length; i++) {
ret.push(myBar.datasets[0].bars[i].label)
};
console.log("current data:" + ret[bar_index]);
// based on the label you can call any function
}
}
}
);
I modified iecs's answer to work with chartjs 2.7.1
var that = this;
this.chart = new Chart($("#chart"), {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
onClick: function(e, data) {
var ctx = $("#chart")[0].getContext("2d");
var base = that.chart.chartArea.bottom;
var height = that.chart.chart.height;
var width = that.chart.chart.scales['x-axis-0'].width;
var offset = $('#chart').offset().top - $(window).scrollTop();
if(e.pageY > base + offset){
var count = that.chart.scales['x-axis-0'].ticks.length;
var padding_left = that.chart.scales['x-axis-0'].paddingLeft;
var padding_right = that.chart.scales['x-axis-0'].paddingRight;
var xwidth = (width-padding_left-padding_right)/count;
// don't call for padding areas
var bar_index = (e.offsetX - padding_left - that.chart.scales['y-axis-0'].width) / xwidth;
if(bar_index > 0 & bar_index < count){
bar_index = Math.floor(bar_index);
console.log(bar_index);
}
}
}
}
});
The main differences are:
The newer versions of chartjs use an chart.scales array instead of chart.scale with a bunch of values
I had to subtract chart.scales['y-axis-0'].width from the x offset to get the correct bar_index
I changed parseInt to Math.floor, just personal preference
And if you want the cursor to change when you hover over them, add "hover" to the events array and this to the options:
onHover: function(e) {
var ctx = $("#chart")[0].getContext("2d");
var base = that.chart.chartArea.bottom;
var height = that.chart.chart.height;
var width = that.chart.chart.scales['x-axis-0'].width;
var yOffset = $('#chart').offset().top - $(window).scrollTop();
var xOffset = $('#chart').offset().left - $(window).scrollLeft();
var left = xOffset + that.chart.scales['x-axis-0'].paddingLeft + that.chart.scales['x-axis-0'].left;
var right = xOffset + that.chart.scales['x-axis-0'].paddingRight + that.chart.scales['x-axis-0'].left + width;
if(e.pageY > base + yOffset && e.pageX > left && e.pageX < right){
e.target.style.cursor = 'pointer';
}
else {
e.target.style.cursor = 'default';
}
}

Offset Overlay Line on chart.js Graph

I'm using the Chart.js fork by Quince (leighquince/Chart.js) and was wondering is there any way to offset the line graph dots like I have illustrated on the first two dots here:
I think I would need a second x-axis.
Just swap your the draw function of the Overlay chart type to add your offset, like so
Chart.types.Overlay.extend({
// Passing in a name registers this chart in the Chart namespace in the same way
name: "OverlayAlt",
draw: function(ease) {
// most of this is from Quince's draw function
var easingDecimal = ease || 1;
this.clear();
this.scale.draw(easingDecimal);
Chart.types.Bar.prototype.drawDatasets.call(this, this.barDatasets, easingDecimal);
// here we just swap out the calculateX function after we draw the scale
var originalScaleDraw = this.scale.draw;
var originalCalculateX = this.scale.calculateX;
var scale = this.scale;
var offset = (scale.calculateX(2) - scale.calculateX(1)) / 2;
scale.draw = function() {
originalScaleDraw.apply(this, arguments);
scale.calculateX = function() {
return originalCalculateX.apply(this, arguments) + offset;
}
}
Chart.types.Line.prototype.drawDatasets.call(this, this.lineDatasets, easingDecimal);
this.scale.draw = originalScaleDraw;
this.scale.calculateX = originalCalculateX;
},
});
Fiddle (updated version of the one in your comment) - http://fiddle.jshell.net/4tk3aa9e/

Using easelJS to free transform shapes on pressmove

I want to replicate the basic functionality of a free transform tool (no rotation), by dragging on the border of a easeljs Shape and adjusting the container to match it. I'm currently using the scaleX and scaleY properties and it sort of works but is not quite right.
If you do one scaling transformation it works pretty well. However if you release, then do another scaling transformation, it jumps very glitchily, and can occasionally break sending the x/y coordinates all the way to stage 0. Any help on this issue would be great!
http://jsfiddle.net/frozensoviet/dsczvrpw/13/
//circle
var circle = new createjs.Shape(new createjs.Graphics()
.beginFill("#b2ffb2")
.drawCircle(0, 0, 50));
circle.setBounds(0, 0, 50, 50);
//create the border as a seperate object
var cBorder = new createjs.Shape(new createjs.Graphics().setStrokeStyle(10)
.beginStroke("#000").drawCircle(0, 0, 50));
cBorder.setBounds(0, 0, 50, 50);
//add both to the container
circleContainer.addChild(circle);
circleContainer.addChild(cBorder);
var cWidth = circleContainer.getBounds().width;
var cHeight = circleContainer.getBounds().height;
//find initial mouse position relative to circle center
cBorder.on("mousedown", function (evt) {
//initial mouse pos
this.initial = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
});
//set the relevant circle axis scale to ratio of mouse pos/initial mouse pos
cBorder.on("pressmove", function (evt) {
//current moouse pos
this.offset = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
if (this.initial.x > this.initial.y) {
//sides
circleContainer.scaleX = this.offset.x / this.initial.x;
} else if (this.initial.x < this.initial.y) {
//top/bottom
circleContainer.scaleY = this.offset.y / this.initial.y;
} else {
//diagonals
circleContainer.scaleX = this.offset.x / this.initial.x;
circleContainer.scaleY = this.offset.y / this.initial.y;
}
stage.update();
});
The issue is your initial calculations don't account for the change in the scale of the circle. You would have to transform the coordinates using localToGlobal. Fortunately, there is an even easier way:
this.initial = {
x: Math.abs(evt.localX),
y: Math.abs(evt.localY)
};
You can also turn on ignoreScale on the border, which makes it not stretch:
createjs.Graphics().setStrokeStyle(10,null,null,null,true) // The 5th argument
Lastly, your bounds setting might work for your demo, but it is not correct. Your circle draws from the center, so it should be:
cBorder.setBounds(-25, -25, 50, 50);
Here is an updated fiddle: http://jsfiddle.net/tfy1sjnj/3/

Categories