円グラフ!
アニメーション処理が問題だ。全部消すのではなく、合成を利用して一部だけを消している
Manager は、マウスの動きと円グラフを管理する
PieChart は、個々の弧を管理する
Arc は、弧
あれから transform を利用して処理するように変更した
<!DOCTYPE html> <html lang="ja"> <title>Test</title> <meta charset="utf-8"> <style> canvas { background : #FFC; } </style> <body> <canvas id="HOGE" width="800" height="400"> canvas による描画 </canvas> <script> (function () { var DEG = Math.PI / 180; var HOVER_SCALE = 1.5; var HOVER_MOVE = 20; //px var HOVER_STEP = 10; function getElementPosition (e) { for (var x = 0, y = 0; e; e = e.offsetParent) { x += e.offsetLeft; y += e.offsetTop; } return { 'x': x, 'y': y }; } function Arc (ctx, x, y, r, sq0, sq1, data) { this.ctx = ctx; this.x = x; this.y = y; this.r = r; this.sq0 = sq0; this.sq1 = sq1; this.sq2 = (sq0 + sq1) / 2; this.data = data; this.timerId = null; this.direction = 0; this.scale = 1; } Arc.prototype.check = function (x, y) { var result; var ctx = this.ctx; ctx.save (); this.trace (); result = ctx.isPointInPath (x, y); ctx.restore (); this.direction = (result) ? 1: -1; if (! this.timerId) this.timerId = setInterval (changer, 50, this); }; Arc.prototype.trace = function () { var ctx = this.ctx; var sin = Math.sin (this.sq1) * this.scale; var cos = Math.cos (this.sq1) * this.scale; ctx.beginPath (); ctx.transform (cos, sin, -sin, cos, this.x, this.y); ctx.moveTo (0,0); ctx.arc (0, 0, this.r, 0, this.sq0, false); ctx.closePath (); }; Arc.prototype.draw = function () { var ctx = this.ctx; ctx.save (); ctx.fillStyle = this.data.color; ctx.strokeStyle = 'black'; this.trace (); ctx.fill (); ctx.stroke (); ctx.restore (); }; function changer (that) { var ctx = that.ctx; ctx.globalCompositeOperation = 'destination-out'; ctx.save (); ctx.fillStyle = that.data.color; ctx.strokeStyle = 'black'; ctx.lineWidth = 2; ctx.beginPath (); that.trace (); ctx.closePath (); ctx.fill (); ctx.stroke (); ctx.restore (); that.scale += that.direction * (HOVER_SCALE / HOVER_STEP); if (that.scale < 1) { that.scale = 1; clearInterval (that.timerId); that.timerId = null; } else if (HOVER_SCALE < that.scale) { that.scale = HOVER_SCALE clearInterval (that.timerId); that.timerId = null; } ctx.globalCompositeOperation = 'source-over'; ctx.save (); ctx.fillStyle = that.data.color; ctx.strokeStyle = 'black'; ctx.beginPath (); that.trace (); ctx.closePath (); ctx.fill (); ctx.stroke (); ctx.restore (); } Arc.create = function (ctx, x, y, r, sq0, sq1, data) { var obj = new Arc (ctx, x, y, r * data.radius / 100, (sq0) * DEG, (sq1) * DEG, data); obj.draw (); return obj; }; //________________________ function PieChart (canvas, ctx, offsetX, offsetY, radius, data) { this.canvas = canvas; this.ctx = ctx; this.offsetX = offsetX; this.offsetY = offsetY; this.radius = radius; this.data = data; this.parts = []; } PieChart.prototype.check = function (parm) { if (this.canvas === parm.canvas) this.parts.forEach (function (p) { p.check (this.x, this.y) }, parm); }; PieChart.prototype.draw = function () { var total = this.data.reduce (function (r, d) { return r + d.value; }, 0); var square0 = 0; var square1 = -90; var i = 0; var d; var obj; this.parts = []; while (d = this.data[i++]) { square0 = 360 * (d.value / total); obj = Arc.create (this.ctx, this.offsetX, this.offsetY, this.radius, square0, square1, d); this.parts.push (obj); square1 += square0; } }; PieChart.create = function (canvas, offsetX, offsetY, radius, data) { if (arguments.length < 5) return null; var ctx = canvas.getContext ('2d'); var chart = new PieChart (canvas, ctx, offsetX, offsetY, radius, data); chart.draw (); return chart; }; this.PieChart = PieChart; //_______________________ var Manager = new function () { this.x = null; this.y = null; this.stock = []; }; Manager.check = function (e, offset) { this.stock.forEach (function (s) { s.check (this);}, { canvas: e, x: this.x - offset.x, y: this.y - offset.y }); }; Manager.add = function (obj) { this.stock.push (obj); }; Manager.handleEvent = function (event) { var e = event.target; var v = e.ownerDocument.defaultView; var offset = { x: null, y: null }; switch (event.type) { case 'mousemove' : case 'mouseover' : this.x = event.clientX + v.pageXOffset; this.y = event.clientY + v.pageYOffset; if ('CANVAS' === e.nodeName) this.check (e, getElementPosition (e)); break; case 'click' : break; } }; document.addEventListener ('mousemove', Manager, false); document.addEventListener ('mouseover', Manager, false); document.addEventListener ('click', Manager, false); this.Manager = Manager; })(); //__________________________ var data = [ { caption: 'A', value: 512, radius: 90, color: '#080' }, { caption: 'B', value: 6534, radius: 60, color: '#f00' }, { caption: 'C', value: 3056, radius: 30, color: '#008' } ]; var data2 = [ { caption: 'A', value: 12, radius: 40, color: '#080' }, { caption: 'B', value: 34, radius: 80, color: '#f00' }, { caption: 'C', value: 56, radius: 20, color: '#008' } ]; var canvas = document.getElementById ('HOGE'); Manager.add (PieChart.create (canvas, 200, 200, 180, data)); Manager.add (PieChart.create (canvas, 500, 200, 180, data2)); </script>