问题
How do i bind onclick event to piechart segment?
https://github.com/sauminkirve/HTML5/blob/master/PieChart/piechart.html
回答1:
A pie chart segment is really a wedge. You have several ways to hit-test a wedge.
One way is the math way:
Test if the mouse is within the radius of a circle created by the wedges.
If the radius test is true, then calculate the angle of the mouse versus the circle's centerpoint.
Compare that angle to each wedge. If the angle is between the starting and ending angle of a specific wedge's arc, then the mouse is inside that wedge.
Another way is to use canvas's built in path hit-testing method: isPointInPath
Redefine one wedge. There's no need to actually stroke or fill that wedge. Just do the commands from
beginPath
toclosePath
.Use
context.isPointInPath(mouseX,mouseY)
to hit-test if the mouse is inside that wedge.If isPointInPath returns true, you've discovered the wedge under the mouse. If not, then redefine & hit-test each of the other wedges.
Here's something I coded a while back that hit-tests the wedges of a pie chart when hovering and moves the wedge out of the pie when a wedge is clicked.
It uses the isPointInPath
method to do the hit-testing:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.lineJoin = "round";
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
function Wedge(cx, cy, radius, startAngleDeg, endAngleDeg, fill, stroke, linewidth) {
this.cx = cx;
this.cy = cy;
this.radius = radius;
this.startAngle = startAngleDeg * Math.PI / 180;
this.endAngle = endAngleDeg * Math.PI / 180;
this.fill = fill;
this.stroke = stroke;
this.lineWidth = linewidth;
this.offsetX = 0;
this.offsetY = 0;
this.rr = radius * radius;
this.centerX = cx;
this.centerY = cy;
this.midAngle = this.startAngle + (this.endAngle - this.startAngle) / 2;
this.offsetDistance = 15;
this.explodeX = this.offsetDistance * Math.cos(this.midAngle);
this.explodeY = this.offsetDistance * Math.sin(this.midAngle);
this.isExploded = false;
};
Wedge.prototype.draw = function(fill, stroke) {
this.define();
this.fillStroke(fill, stroke);
ctx.beginPath();
ctx.arc(this.cx, this.cy, this.radius, 0, Math.PI * 2);
ctx.closePath();
ctx.lineWidth = 0.50;
ctx.stroke();
}
Wedge.prototype.fillStroke = function(fill, stroke) {
ctx.fillStyle = fill || this.fill;
ctx.fill();
ctx.strokeStyle = stroke, this.stroke;
ctx.lineWidth = this.lineWidth;
ctx.stroke();
}
Wedge.prototype.define = function() {
var x = this.cx + this.offsetX;
var y = this.cy + this.offsetY;
ctx.beginPath();
ctx.arc(x, y, this.radius, this.startAngle, this.endAngle);
ctx.lineTo(x, y);
ctx.closePath();
}
Wedge.prototype.ptAtAngle = function(radianAngle) {
var xx = (this.cx + this.offsetX) + this.radius * Math.cos(radianAngle);
var yy = (this.cy + this.offsetY) + this.radius * Math.sin(radianAngle);
return ({
x: x,
y: y
});
}
Wedge.prototype.explode = function(isExploded) {
this.isExploded = isExploded;
this.offsetX = isExploded ? this.explodeX : 0;
this.offsetY = isExploded ? this.explodeY : 0;
this.draw();
}
Wedge.prototype.isPointInside = function(x, y) {
var dx = x - (this.cx + this.offsetX);
var dy = y - (this.cy + this.offsetY);
if (dx * dx + dy * dy > this.rr) {
return (false);
}
var angle = (Math.atan2(dy, dx) + Math.PI * 2) % (Math.PI * 2);
return (angle >= this.startAngle && angle <= this.endAngle);
}
Wedge.prototype.marker = function(pos) {
ctx.beginPath();
ctx.arc(pos.x, pos.y, 3, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = "red";
ctx.fill();
}
function handleMouseDown(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
clear();
for (var i = 0; i < wedges.length; i++) {
var wedge = wedges[i].wedge;
if (wedge.isPointInside(mouseX, mouseY)) {
wedge.explode(!wedge.isExploded);
}
wedge.draw();
}
}
function handleMouseUp(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mouseup stuff here
isDown = false;
}
function handleMouseOut(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mouseOut stuff here
isDown = false;
}
function handleMouseMove(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
for (var i = 0; i < wedges.length; i++) {
var wedge = wedges[i].wedge;
if (wedge.isPointInside(mouseX, mouseY)) {
wedge.draw("black");
} else {
wedge.draw();
}
}
}
$("#canvas").mousedown(function(e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function(e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function(e) {
handleMouseUp(e);
});
$("#canvas").mouseout(function(e) {
handleMouseOut(e);
});
function clear() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
var PI2 = Math.PI * 2;
var cx = 150;
var cy = 150;
var r = 100;
var line = 2;
var stroke = "black";
var wedges = [];
wedges.push({
percent: 18,
fill: "red"
});
wedges.push({
percent: 30,
fill: "blue"
});
wedges.push({
percent: 25,
fill: "green"
});
wedges.push({
percent: 13,
fill: "purple"
});
wedges.push({
percent: 14,
fill: "gold"
});
var rAngle = 0;
for (var i = 0; i < wedges.length; i++) {
var wedge = wedges[i];
var angle = 360 * wedge.percent / 100;
wedge.wedge = new Wedge(cx, cy, r, rAngle, rAngle + angle, wedge.fill, "black", 1);
wedge.wedge.draw();
rAngle += angle;
}
window.onscroll = function(e) {
var BB = canvas.getBoundingClientRect();
offsetX = BB.left;
offsetY = BB.top;
}
body {
background-color: ivory;
}
#canvas {
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Hover wedge to highlight it<br>Click wedge to explode that wedge</h4>
<canvas id="canvas" width=300 height=300></canvas>
来源:https://stackoverflow.com/questions/29034251/how-do-i-bind-onclick-event-to-piechart-segment