Related
I've got the doughnut part of the chart complete and the gauge needle. I want to add this circular pointer on the doughnut instead of the needle. I was able to draw the circular pointer but couldn't find the right X,Y coordinates to place the pointer.
Here is the DEMO
Here in the below image, the circle should be placed at the gauge needle pointer
The code I've used is the following for the circular pointer.
const pointer = {
id: "pointer",
afterDatasetsDraw: (chart) => {
const { ctx } = chart;
var data = chart._metasets[0].data[0];
var radius = data.innerRadius + (data.outerRadius - data.innerRadius) / 2;
var centerX = data.x;
var centerY = data.y;
const angle = (180 / 1000) * speed;
// this thing needs to be fixed
var x = centerX + radius * Math.cos(angle * Math.PI);
var y = centerY + radius * Math.sin(angle * Math.PI);
ctx.save();
ctx.beginPath();
ctx.lineWidth = 6;
ctx.arc(x, y, 12, 0, 2 * Math.PI);
ctx.stroke();
ctx.restore();
}
};
Target to achive:
Basically you want 75% of 180 Degrees (because the speed = 75):
const angle = Math.PI * ( speed / 100) + Math.PI;
And than Math.cos and Math.sin expect a radiant value(link to mdn documentation), which you already have, so no multiplication with Math.PI is needed anymore.
var x = centerX + radius * Math.cos( angle );
var y = centerY + radius * Math.sin( angle );
Full working demo (Updated Example, now with animation):
const speed = 75;
let animationAngle = 0;
var pointer = {
id: 'pointer',
defaults:{
percentage: 0,
maxAngle: 0
},
afterDraw: function(chart, args, opt) {
const { ctx } = chart;
var data = chart._metasets[0].data[0];
var radius = data.innerRadius + (data.outerRadius - data.innerRadius) / 2;
var centerX = data.x;
var centerY = data.y;
const angle = (Math.PI * ( speed / 100) * chart.options.plugins.pointer.percentage) + Math.PI;
var x = centerX + radius * Math.cos( angle );
var y = centerY + radius * Math.sin( angle );
ctx.save();
ctx.beginPath();
ctx.lineWidth = 6;
ctx.arc(x, y, 12, 0, 2 * Math.PI);
ctx.stroke();
ctx.restore();
},
}
var options = {
type: 'doughnut',
data: {
datasets: [{
data: [20, 50, 30],
backgroundColor: [
'rgba(231, 76, 60, 1)',
'rgba(255, 164, 46, 1)',
'rgba(46, 204, 113, 1)'
],
borderColor: [
'rgba(255, 255, 255 ,1)',
'rgba(255, 255, 255 ,1)',
'rgba(255, 255, 255 ,1)'
],
borderWidth: 0
}]},
options: {
cutout: 80,
rotation: -90,
circumference: 180,
animation:{
onProgress: function(context){
if(context.initial){
this.options.plugins.pointer.percentage = context.currentStep / context.numSteps;
}
}
},
maintainAspectRatio: false,
legend: { display: false },
plugins:{
tooltip: { enabled: false },
pointer: {currentAngle: 1}
}
},
plugins:[pointer]
}
const chart = document.getElementById('chart1')
new Chart(chart, options);
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div style="width:500px;height:184px">
<canvas id="chart1" width="500" height="184"></canvas>
<div>
I am working with a custom gauge using chart.js doughnut type and it's working fine. Now I am trying to change the color of each elements of gauge when the theme button is triggered. So, I have created a update function to do the task. But the plugins are updating but inversely. Also, don't know how to get the needleColor plugin path location for changing the needle color dynamically.
Images are linked below:
lightmood-without-trigger
darkmood-without-trigger
lightmood-after-trigger-theme-button-from-night-to-day
darkmood-after-trigger-theme-button-from-day-to-night
HTML
<div class="gauge_container">
<canvas class="gauge" id="gauge"> </canvas>
</div>
<i onclick="changeColor();" class="uil uil-moon change-theme" id="theme-button"
><span class="theme-name">Dark</span>
</i>
CSS
:root {
--hue-color: 5;
--gauge-color: hsl(var(--hue-color), 69%, 61%);
--gauge-color-border: #fff;
}
body.dark-theme {
--gauge-color: #fff;
--gauge-color-border: hsl(var(--hue-color), 30%, 8%);
}
.gauge_container {
width: 250px;
height: 200px;
margin-left: auto;
margin-right: auto;
}
JavaScript
const gaugeStyle = getComputedStyle(document.body);
var gaugeColor = gaugeStyle.getPropertyValue("--gauge-color");
var gaugeColorBorder = gaugeStyle.getPropertyValue("--gauge-color-border");
function changeColor() {
var gaugeColor = gaugeStyle.getPropertyValue("--gauge-color");
var gaugeColorBorder = gaugeStyle.getPropertyValue("--gauge-color-border");
myGauge.options.plugins.tooltip.backgroundColor = gaugeColor;
myGauge.options.plugins.tooltip.bodyColor = gaugeColorBorder;
myGauge.data.datasets[0].borderColor = gaugeColorBorder;
// Don't know how to get the needle color location path
myGauge.update();
}
const gaugeData = {
labels: ["Safe", "Risky", "High Risk"],
datasets: [
{
label: "Gauge",
data: [100, 65, 35],
backgroundColor: [
"rgba(75, 192, 192, 0.8)",
"rgba(255, 206, 86, 0.8)",
"rgba(255, 26, 104, 0.8)",
],
needleValue: 50,
borderColor: gaugeColorBorder,
borderWidth: 1,
cutout: "95%",
circumference: 180,
rotation: 270,
borderRadius: 5,
},
],
};
//gaugeNeedle
const gaugeNeedle = {
id: "gaugeNeedle",
afterDatasetDraw(chart) {
const {
ctx,
data,
chartArea: { width, height },
} = chart;
ctx.save();
const needleValue = data.datasets[0].needleValue;
if (needleValue <= 100) {
var angle = Math.PI + (1 / 200) * needleValue * Math.PI;
} else if (needleValue <= 10000) {
var angle =
Math.PI +
(1 / 200) * 100 * Math.PI +
((1 / 200) * needleValue * Math.PI * 65) / 10000;
} else if (needleValue <= 1000000) {
var angle =
Math.PI +
(1 / 200) * 100 * Math.PI +
((1 / 200) * 10000 * Math.PI * 65) / 10000 +
((1 / 200) * needleValue * Math.PI * 35) / 1000000;
} else {
var angle = 0;
}
const cx = width / 2;
const cy = chart._metasets[0].data[0].y;
//needle
ctx.translate(cx, cy);
ctx.rotate(angle);
ctx.beginPath();
ctx.moveTo(0, -2);
ctx.lineTo(height - ctx.canvas.offsetTop - 130, 0);
ctx.lineTo(0, 2);
ctx.fillStyle = gaugeColor; // Don't how to get this in changeColor function
ctx.fill();
//needle dot
ctx.translate(-cx, -cy);
ctx.beginPath();
ctx.arc(cx, cy, 5, 0, 10);
ctx.fill();
ctx.restore();
//text
ctx.font = "20px Ubuntu";
ctx.fillStyle = gaugeColor;
ctx.fillText(needleValue + " CPM", cx, cy + 50);
ctx.font = "10px Ubuntu";
ctx.fillText(0, 3, cy + 20);
ctx.fillText(100, cx, 50);
ctx.fillText("10k", cx + 115, 115);
ctx.fillText("1M", cx + 118, 205);
ctx.textAlign = "center";
ctx.restore();
},
};
// config
const gaugeConfig = {
type: "doughnut",
data: gaugeData,
options: {
plugins: {
legend: {
display: false,
},
tooltip: {
yAlign: "bottom",
displayColors: false,
callbacks: {
label: function (tooltipItem) {
return tooltipItem.label;
},
},
backgroundColor: gaugeColor,
bodyColor: gaugeColorBorder,
},
},
},
plugins: [gaugeNeedle],
};
// render init block
var myGauge = new Chart(document.getElementById("gauge"), gaugeConfig);
I have an application using HTML5 canvas via Fabric.js. And I would like to do arrow with text. Then moving arrow the text is in correct position, but is problem with rotation:
incorrect text position:
text position have to be:
My code is:
var canvas = new fabric.Canvas('c');
var line = new fabric.Line([10, 50, 200, 50], {
stroke: 'black',
strokeWidth: 3,
strokeDashArray: [10, 5]
});
var line2 = new fabric.Line([199, 50, 180, 60], {
stroke: 'black',
strokeWidth: 3
});
var line3 = new fabric.Line([199, 50, 180, 40], {
stroke: 'black',
strokeWidth: 3
});
var strele = new fabric.Group([line, line2, line3], {});
tekst = new fabric.IText('<<extend>>', {
left: 50,
top: 20,
fontFamily: 'Arial',
fill: '#333',
fontSize: 20
});
canvas.add(strele, tekst);
function onMoving(options) {
if (options.target === strele) {
tekst.left = strele.left + ((strele.width * strele.scaleX) / 2) - (tekst.width * tekst.scaleX) / 2;
tekst.top = strele.top - strele.height * strele.scaleY;
} else if (options.target === tekst) {
strele.left = tekst.left - ((strele.width * strele.scaleX) / 2 - (tekst.width * tekst.scaleX) / 2);
strele.top = tekst.top + strele.height * strele.scaleY;
}
canvas.forEachObject(function(o) {
o.setCoords()
});
}
function onRotating(options) {
if (options.target === strele) {
tekst.angle = strele.angle;
tekst.left = strele.left((strele.width * strele.scaleX) / 2) - (tekst.width * tekst.scaleX) / 2;
tekst.top = strele.top - strele.height * strele.scaleY;
}
}
canvas.on('object:moving', onMoving);
canvas.on('object:rotating', onRotating);
My code in fiddle
How can I fix this?
Here is an approach that closely resembles your image.
To simplify the calculations I changed the objects origin(X & Y) to center, the rest is just some math and conditions for the angles. My approach was to keep the text readable and not overlap the line.
var canvas = new fabric.Canvas('c');
var line = new fabric.Line([160, 100, 350, 100], {stroke: 'black', strokeWidth: 3, strokeDashArray: [10, 5]});
var line2 = new fabric.Line([350, 100, 330, 110], {stroke: 'black', strokeWidth: 3});
var line3 = new fabric.Line([350, 100, 330, 90], {stroke: 'black', strokeWidth: 3});
var strele = new fabric.Group([line, line2, line3], {originX: "center", originY: "center"});
var tekst = new fabric.IText('<<extend>>', {left: 160, top: 70, fontFamily: 'Arial',fill: 'red', fontSize: 20, selectable: false,
originX: "center", originY: "center"
});
canvas.add(strele, tekst);
canvas.setActiveObject(canvas.item(0));
canvas.on('object:moving', onMoving);
canvas.on('object:rotating', onRotating);
function onMoving() {
tekst.left = strele.left + 20 * Math.sin(strele.angle * Math.PI /180);
tekst.top = strele.top - 20 * Math.cos(strele.angle * Math.PI /180);
}
function onRotating() {
var ang = strele.angle % 360;
if (ang < 270) {
if (ang > 180) {
ang = strele.angle % 180;
} else if (ang > 90) {
ang = 180+strele.angle;
}
}
tekst.angle = ang
onMoving()
}
function rotate() {
strele.angle += 22.2;
strele.setCoords()
onRotating()
canvas.renderAll();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.4/fabric.min.js"></script>
<button id="rotate" onclick="rotate()">rotate</button>
<canvas id="c" width="600" height="600"></canvas>
Hey I have a canvas element which I am rotate 180 degree using the following code:
-moz-transform: scale(-1, 1);
-webkit-transform: scale(-1, 1);
-o-transform: scale(-1, 1);
transform: scale(-1, 1);
filter: FlipH;
The canvas is get rotated but the text is flipped out.
Here is the the result:
I want to flip the text, after I have rotate the canvas 180 degree,apparently the text get rotate 180 too and I want to reverse it.
I want to rotate only the text and not all the other elements.
The original code is here:
go to ticklines function
http://codepen.io/Barak/pen/PNXROK
$(document).ready(function() {
$(function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.transform(1, 0, 0, -1, 0, canvas.height);
// https://www.researchgate.net/publication/4345236_A_Software_Implementation_of_the_Duval_Triangle_Method
var v0 = {
x: 50 * 10,
y: (86.603 * 10)
};
var v1 = {
x: 0 * 10,
y: (0 * 10)
};
var v2 = {
x: 100 * 10,
y: (0 * 10)
};
var triangle = [v0, v1, v2];
// Define all your segments here
var segments = [{
points: [{
x: 0 * 10,
y: 0 * 10
}, {
x: 43.5 * 10,
y: 75.34461 * 10
}, {
x: 55 * 10,
y: 55.42 * 10
}, {
x: 23 * 10,
y: 0 * 10
}],
fill: 'rgb(172,236,222)',
label: {
text: '',
cx: 300,
cy: 645,
withLine: false,
endX: null,
endY: null
},
}, {
points: [{
x: 23 * 10,
y: 0 * 10
}, {
x: 55 * 10,
y: 55.42 * 10
}, {
x: 63.5 * 10,
y: 40.70 * 10
}, {
x: 55.5 * 10,
y: 26.84 * 10
}, {
x: 71 * 10,
y: 0 * 10
}],
fill: 'deepskyblue',
label: {
text: '',
cx: 490,
cy: 645,
withLine: false,
endX: null,
endY: null
},
}, {
points: [{
x: 71 * 10,
y: 0 * 10
}, {
x: 55.5 * 10,
y: 26.84693 * 10
}, {
x: 63.5 * 10,
y: 40.70341 * 10
}, {
x: 43.5 * 10,
y: 75.34461 * 10
}, {
x: 48 * 10,
y: 83.13888 * 10
}, {
x: 73 * 10,
y: 39.83737 * 10
}, {
x: 67.5 * 10,
y: 30.311 * 10
}, {
x: 85 * 10,
y: 0 * 10
}],
fill: 'lightCyan',
label: {
text: '',
cx: 656,
cy: 645,
withLine: false,
endX: 366,
endY: 120
},
}, { //here - I am in hell.-s5
points: [{
x: 49 * 10,
y: 84.87093999999999 * 10
}, {
x: 50 * 10,
y: 86.603 * 10
}, {
x: 51 * 10,
y: 84.87093999999999 * 10
}],
fill: 'black',
label: {
text: 'PD',
cx: 600,
cy: 52,
withLine: true,
endX: 520,
endY: 70
},
}, {
points: [{
x: 48 * 10,
y: 83.1388 * 10
}, {
x: 49 * 10,
y: 84.87093 * 10
}, {
x: 51 * 10,
y: 84.87093 * 10
}, {
x: 60 * 10,
y: 69.2824 * 10
}, {
x: 58 * 10,
y: 65.81828 * 10
}],
fill: 'navajoWhite',
label: {
text: '',
cx: 670,
cy: 140,
withLine: true,
endX: 574,
endY: 105
},
}, {
points: [{
x: 58 * 10,
y: 65.81828 * 10
}, {
x: 60 * 10,
y: 69.2824 * 10
}, {
x: 75 * 10,
y: 43.3015 * 10
}, {
x: 73 * 10,
y: 39.837 * 10
}],
fill: 'tan',
label: {
text: '',
cx: 800,
cy: 290,
withLine: true,
endX: 662,
endY: 120
},
}, {
points: [{
x: 85 * 10,
y: 0 * 10
}, {
x: 67.5 * 10,
y: 30.311 * 10
}, {
x: 75 * 10,
y: 43.3015 * 10
}, {
x: 100 * 10,
y: 0
}],
fill: 'peru',
label: {
text: '',
cx: 800,
cy: 645,
withLine: false,
endX: null,
endY: null
},
}, ];
// label styles
var labelfontsize = 12;
var labelfontface = 'verdana';
var labelpadding = 3;
// pre-create a canvas-image of the arrowhead
var arrowheadLength = 10;
var arrowheadWidth = 8;
var arrowhead = document.createElement('canvas');
premakeArrowhead();
var legendTexts = ['PD = Partial Discharge',
'DT = Discharges and Thermal',
'T1 = Thermal fault T < 300 ℃',
'T2 = Thermal fault 300 ℃ < T < 700 ℃',
'T3 = Thermal fault T > 700 ℃',
'D1 = Discharges of low energy',
'D2 = Discharges of high energy'
];
// start drawing
/////////////////////
// draw colored segments inside triangle
for (var i = 0; i < segments.length; i++) {
drawSegment(segments[i]);
}
// draw ticklines
//
ticklines(v0, v1, 9, Math.PI * 1.2, 20);
ticklines(v1, v2, 9, Math.PI * 3 / 4, 20);
ticklines(v2, v0, 9, Math.PI * 2, 20);
// molecules
// moleculeLabel(v0, v1, 100, Math.PI / 2, '% CH4');
// moleculeLabel(v1, v2, 100, 0, '% C2H4');
// moleculeLabel(v2, v0, 100, Math.PI, '% C2H2');
// draw outer triangle
drawTriangle(triangle);
// draw legend
//drawLegend(legendTexts, 10, 10, 12.86);
drawCircle(canvas.width / 3, canvas.height / 2, 2.5, 'red');
// end drawing
/////////////////////
function drawCircle(point1, point2, radius, color) {
ctx.beginPath();
ctx.arc(point1, point2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = color;
ctx.fill();
}
function drawSegment(s) {
// draw and fill the segment path
ctx.beginPath();
ctx.moveTo(s.points[0].x, s.points[0].y);
for (var i = 1; i < s.points.length; i++) {
ctx.lineTo(s.points[i].x, s.points[i].y);
}
ctx.closePath();
ctx.fillStyle = s.fill;
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
// draw segment's box label
/* if (s.label.withLine) {
lineBoxedLabel(s, labelfontsize, labelfontface, labelpadding);
} else {
boxedLabel(s, labelfontsize, labelfontface, labelpadding);
} */
}
function moleculeLabel(start, end, offsetLength, angle, text) {
ctx.textAlign = 'center';
ctx.textBaseline = 'middle'
ctx.font = '14px verdana';
var dx = end.x - start.x;
var dy = end.y - start.y;
var x0 = parseInt(start.x + dx * 0.50);
var y0 = parseInt(start.y + dy * 0.50);
var x1 = parseInt(x0 + offsetLength * Math.cos(angle));
var y1 = parseInt(y0 + offsetLength * Math.sin(angle));
ctx.fillStyle = 'black';
ctx.fillText(text, x1, y1);
// arrow
var x0 = parseInt(start.x + dx * 0.35);
var y0 = parseInt(start.y + dy * 0.35);
var x1 = parseInt(x0 + 50 * Math.cos(angle));
var y1 = parseInt(y0 + 50 * Math.sin(angle));
var x2 = parseInt(start.x + dx * 0.65);
var y2 = parseInt(start.y + dy * 0.65);
var x3 = parseInt(x2 + 50 * Math.cos(angle));
var y3 = parseInt(y2 + 50 * Math.sin(angle));
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x3, y3);
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.stroke();
var angle = Math.atan2(dy, dx);
ctx.save(); // save
ctx.translate(x3, y3);
ctx.rotate(angle);
ctx.drawImage(arrowhead, -arrowheadLength, -arrowheadWidth / 2);
ctx.restore()
}
function boxedLabel(s, fontsize, fontface, padding) {
var centerX = s.label.cx;
var centerY = s.label.cy;
var text = s.label.text;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle'
ctx.font = fontsize + 'px ' + fontface
var textwidth = ctx.measureText(text).width;
var textheight = fontsize * 1.286;
var leftX = centerX - textwidth / 2 - padding;
var topY = centerY - textheight / 2 - padding;
ctx.fillStyle = 'white';
ctx.fillRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
ctx.lineWidth = 1;
ctx.strokeRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
ctx.fillStyle = 'black';
ctx.fillText(text, centerX, centerY);
}
function lineBoxedLabel(s, fontsize, fontface, padding) {
var centerX = s.label.cx;
var centerY = s.label.cy;
var text = s.label.text;
var lineToX = s.label.endX;
var lineToY = s.label.endY;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle'
ctx.font = fontsize + 'px ' + fontface
var textwidth = ctx.measureText(text).width;
var textheight = fontsize * 1.286;
var leftX = centerX - textwidth / 2 - padding;
var topY = centerY - textheight / 2 - padding;
// the line
ctx.beginPath();
ctx.moveTo(leftX, topY + textheight / 2);
ctx.lineTo(lineToX, topY + textheight / 2);
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.stroke();
// the boxed text
ctx.fillStyle = 'white';
ctx.fillRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
ctx.strokeRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
ctx.fillStyle = 'black';
ctx.fillText(text, centerX, centerY);
}
function ticklines(start, end, count, angle, length) {
var dx = end.x - start.x;
var dy = end.y - start.y;
ctx.lineWidth = 1;
for (var i = 1; i < count; i++) {
var x0 = parseInt(start.x + dx * i / count);
var y0 = parseInt(start.y + dy * i / count);
var x1 = parseInt(x0 + length * Math.cos(180 - angle));
var y1 = parseInt(y0 + length * Math.sin(180 - angle));
ctx.beginPath();
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.stroke();
if (i == 2 || i == 4 || i == 6 || i == 8) {
var labelOffset = length * 3 / 4;
var x1 = parseInt(x0 - labelOffset * Math.cos(180 - angle));
var y1 = parseInt(y0 - labelOffset * Math.sin(180 - angle));
ctx.save();
ctx.fillStyle = 'black';
ctx.rotate(Math.PI);
ctx.fillText(parseInt(i * 10), x1, y1);
ctx.restore();
}
}
}
function premakeArrowhead() {
var actx = arrowhead.getContext('2d');
arrowhead.width = arrowheadLength;
arrowhead.height = arrowheadWidth;
actx.beginPath();
actx.moveTo(0, 0);
actx.lineTo(arrowheadLength, arrowheadWidth / 2);
actx.lineTo(0, arrowheadWidth);
actx.closePath();
actx.fillStyle = 'black';
actx.fill();
}
function drawTriangle(t) {
ctx.beginPath();
ctx.moveTo(t[0].x, t[0].y);
ctx.lineTo(t[1].x, t[1].y);
ctx.lineTo(t[2].x, t[2].y);
ctx.closePath();
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();
}
function drawLegend(texts, x, y, lineheight) {
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillStyle = 'black';
ctx.font = '12px arial';
for (var i = 0; i < texts.length; i++) {
ctx.fillText(texts[i], x, y + i * lineheight);
}
}
})
});
body {
background-color: ivory;
padding: 10px;
}
#canvas {
border: 1px solid red;
margin: 0 auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<canvas id="canvas" width=1024 height=1024></canvas>
</div>
I'm reworking a application for a client, and they want to show a chart. They currently use a infragistics chart, and they would love to stop using that, but the layout must remain the same.
this is the layout they want( so labels should be visible, individual bars, ...)
So my first option was to try out Chart.js, but I could only get something like this:
So basicly the only thing that needs to happen is a way to always show the labels.
The data is provided via angular and is just an array if integers. Using the following directive for the chart: http://jtblin.github.io/angular-chart.js/
My current html:
<canvas id="polar" class="chart chart-polar-area" data="data.values" labels="data.labels" width="200" height="200"
options="{animateRotate: false}"></canvas>
I found this; How to add label in chart.js for pie chart but I couldn't get it to work
Based on ChartJS: Change the positions of the tooltips
Preview
Converting the above to angular-chart is easy because we are only setting the options. However we need to make 2 minor changes (i.e. set chart and ctx variables from this
// THIS IS REQUIRED AND WAS ADDED
tooltipEvents: [],
onAnimationComplete: function () {
// THESE 2 LINES ARE NEW
var chart = this.chart;
var ctx = this.chart.ctx;
this.segments.forEach(function (segment) {
var outerEdge = Chart.Arc.prototype.tooltipPosition.apply({
// THESE 2 LINES WERE CHANGED
x: chart.width / 2,
y: chart.height / 2,
startAngle: segment.startAngle,
endAngle: segment.endAngle,
outerRadius: segment.outerRadius * 2 + 10,
innerRadius: 0
})
...
The entire code assuming you have the library scripts included looks something like this
HTML
<div ng-app="myApp">
<div ng-controller="myController">
<canvas id="polar-area" class="chart chart-polar-area" data="data" labels="labels" options="options"></canvas>
</div>
</div>
Script
angular.module('myApp', ["chart.js"]);
angular.module('myApp').controller('myController', function ($scope) {
$scope.labels = ["Download Sales", "In-Store Sales", "Mail-Order Sales", "Tele Sales", "Corporate Sales"];
$scope.data = [300, 500, 100, 40, 120];
$scope.options = {
scaleOverride: true,
scaleStartValue: 0,
scaleStepWidth: 40,
scaleSteps: 10,
tooltipEvents: [],
onAnimationComplete: function () {
var chart = this.chart;
var ctx = this.chart.ctx;
this.segments.forEach(function (segment) {
var outerEdge = Chart.Arc.prototype.tooltipPosition.apply({
x: chart.width / 2,
y: chart.height / 2,
startAngle: segment.startAngle,
endAngle: segment.endAngle,
outerRadius: segment.outerRadius * 2 + 10,
innerRadius: 0
})
var normalizedAngle = (segment.startAngle + segment.endAngle) / 2;
while (normalizedAngle > 2 * Math.PI) {
normalizedAngle -= (2 * Math.PI)
}
if (normalizedAngle < (Math.PI * 0.4) || (normalizedAngle > Math.PI * 1.5))
ctx.textAlign = "start";
else if (normalizedAngle > (Math.PI * 0.4) && (normalizedAngle < Math.PI * 0.6)) {
outerEdge.y += 5;
ctx.textAlign = "center";
}
else if (normalizedAngle > (Math.PI * 1.4) && (normalizedAngle < Math.PI * 1.6)) {
outerEdge.y - 5;
ctx.textAlign = "center";
}
else
ctx.textAlign = "end";
ctx.fillText(segment.label, outerEdge.x, outerEdge.y);
})
}
}
}
);
Fiddle - http://jsfiddle.net/tmzpy7Lt/