- ベストアンサー
html5で複雑な円グラフを描画
- みんなの回答 (13)
- 専門家の回答
質問者が選んだベストアンサー
html5ならcanvasが使えるのでは? 扇形を作るものを用意しておけば、それで事足りそう。 html5もcanvasも使ったことはありませんが、お勉強のためにサンプルを。 参考にしたサイト https://developer.mozilla.org/ja/Canvas_tutorial fx6.0でしか確認していません。 数値の意味が不明でしたので、角度などは適当です。 (全角空白は半角に) <!DOCTYPE html> <html lang="ja"> <head> <title>sample</title> </head> <body> <canvas id="graph" width="400" height="400"></canvas> <script type="text/javascript"> (function(){ var fanShape = (function(rad){ return function(ctx, r, ang1, ang2, col, frame){ ctx.fillStyle = col; ctx.beginPath(); ctx.moveTo(0, 0); ctx.arc(0, 0, r, rad(ang1), rad(ang2)); ctx.fill(); if(frame){ ctx.closePath(); ctx.stroke(); } } })(function(a){ return Math.PI*(a-90)/180; }); var context, graph = document.getElementById("graph"); if(graph.getContext && (context = graph.getContext("2d"))){ context.translate(200, 200); fanShape(context, 180, 0, 360, "#ddd"); fanShape(context, 160, 0, 10, "#0c0", true); fanShape(context, 90, 10, 240, "#c00", true); fanShape(context, 60, 240, 360, "#00c", true); } })(); </script> </body> </html>
その他の回答 (12)
- dscripty
- ベストアンサー率51% (166/325)
[ANo.10] 改変 まじめに SVG 要素でアニメーションバージョン。 https://ideone.com/l5Rjx Firefox 8 だと意図したとおりに気持ちわるく動いてくれるけど、 Google Chrome 15 だと、mouseover と mouseout の順番が逆転してヘンテコ。 そういえば、円グラフの項目が一つで 100% のときはどうするか考えてないけど、 やっぱり『動かない』が正解かなぁ?
お礼
ありがとうございます。 いまひとつひとつ確認をさせてもらっているのですが、firefox8だとうまくうごきますね!! 100%の場合確かに「動かない」がいいとはおもいますが、他の比率の場合は「動く」ので「動く」で統一しようかなとはおもってます。
- babu_baboo
- ベストアンサー率51% (268/525)
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; var i = 0; var d; var obj; this.parts = []; while (d = this.data[i++]) { square1 = square0 + (360 / 100) * (d.value / total) * 100; obj = Arc.create (this.ctx, this.offsetX, this.offsetY, this.radius, square0, square1, d); this.parts.push (obj); square0 = square1; } }; 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) { var offset = getElementPosition (e); var parm = { canvas: e, x: this.x - offset.x, y: this.y - offset.y }; this.stock.forEach (function (s) { s.check (this); }, parm); }; 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); } 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, 400, 200, 180, data2)); </script> ぜんかくくうはくは、はんかくに。 そして、「弧」のぶぶんは、いつものようにちからつきてぐだぐだです。 わたしも、Fujillinさんとどうよう、みようみまねでかいているので、ごしどうのほど・・・
お礼
ご丁寧にありがとうございます。 週末にかけてみなさんの書いて頂いたコードをじっくり勉強させて頂きます。 ありがとうございます。
- babu_baboo
- ベストアンサー率51% (268/525)
かなりじかんをさいて canvas でかいてみた。 ぜんたいをかいてけすのではなく、うわがきでけしているのですが、ごみがのこります。 くにくのさくとして、せんをふとくしてごまかしています。 たぶん、ながいのでぶんかつです。 <!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 (); ctx.beginPath (); this.trace (); result = ctx.isPointInPath (x, y); ctx.closePath (); ctx.restore (); (result) ? this.focus () : this.focusout (); }; Arc.prototype.trace = function () { var ctx = this.ctx; ctx.translate (this.x, this.y); ctx.scale (this.scale, this.scale); ctx.moveTo (0,0); ctx.arc (0, 0, this.r, this.sq0, this.sq1, false); }; Arc.prototype.draw = function () { var ctx = this.ctx; ctx.save (); ctx.fillStyle = this.data.color; ctx.strokeStyle = 'black'; ctx.beginPath (); this.trace (); ctx.fill (); ctx.closePath (); ctx.stroke (); ctx.restore (); }; Arc.prototype.focus = function () { this.direction = 1; if (! this.timerId) this.timerId = setInterval (changer, 50, this); }; Arc.prototype.focusout = function () { this.direction = -1; if (! this.timerId) this.timerId = setInterval (changer, 50, this); }; 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; } 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 - 90) * DEG, (sq1 - 90) * DEG, data); obj.draw (); return obj; }; //________________________
- dscripty
- ベストアンサー率51% (166/325)
ちょっと時間できたから書いてみた。 <svg id="svg" width="150px" height="150px" viewBox="-150 -150 300 300" xmlns="http://www.w3.org/2000/svg" version="1.1"> <script type="text/ecmascript"><![CDATA[ window.onload = function () { var svg = document.getElementById("svg"); var pie = null; pie = createPieceOfPie(0, 90, 120, { onmouseout:"pie_mouseout(evt);", onmouseover:"pie_mouseover(evt);" , fill:"#fcc", stroke:"white", "stroke-width":"2" }) svg.appendChild(pie); pie = createPieceOfPie(90, 60, 100, { onmouseout:"pie_mouseout(evt);", onmouseover:"pie_mouseover(evt);" , fill:"#ccf", stroke:"white", "stroke-width":"2" }) svg.appendChild(pie); pie = createPieceOfPie(150, 30, 80, { onmouseout:"pie_mouseout(evt);", onmouseover:"pie_mouseover(evt);" , fill:"#cec", stroke:"white", "stroke-width":"2" }) svg.appendChild(pie); pie = createPieceOfPie(180, 180, 60, { onmouseout:"pie_mouseout(evt);", onmouseover:"pie_mouseover(evt);" , fill:"#ccc", stroke:"white", "stroke-width":"2" }) svg.appendChild(pie); } function createSVGElement(tagName, attrs) { var path = document.createElementNS("http://www.w3.org/2000/svg",tagName); for (var name in attrs) { path.setAttribute(name, attrs[name]); } return path; } function createPieceOfPie(start, deg, r, attrs) { // 座標計算が面倒なので、扇形の頂上を y 軸の - 方向に作成 var large_arc_flag = (deg > 180)? 1: 0; var sweep_flag = 1; var rad = Math.PI * deg / 360.0; //角度を半分で計算 var y = -(r * Math.cos(rad)); //座標系が SVG は特殊なので var x = (r * Math.sin(rad)); //座標系が SVG は特殊なので attrs["d"] = "M0,0 L" + (-x).toFixed(3) + "," + y.toFixed(3) + "A" + r + "," + r + " 0 " + large_arc_flag + "," + sweep_flag + " " + x.toFixed(3) + "," + y.toFixed(3) + " z"; var pie = createSVGElement("path", attrs); var g = document.createElementNS("http://www.w3.org/2000/svg","g"); g.appendChild(pie); g.setAttribute("transform", "rotate(" + (deg/2+start) + ")"); return g; } function pie_mouseover(evt) { // 親要素の g を rotate してるから // y 座標を -20 だけ移動すれば、 // 自動で移動して欲しい方向に移動する。 var pie = evt.target; var fill = pie.setAttribute("transform", "translate(0,-20)"); } function pie_mouseout(evt) { var pie = evt.target; pie.removeAttribute("transform"); } ]]></script> </svg>
お礼
どうもありがとうございます。 一度ちょっとじっくりコードをみさせて頂きます!!
- fujillin
- ベストアンサー率61% (1594/2576)
#7です。 やっぱりやってました。 動く方向がたまたま0radになると、hoverの動作が設定されませんね。 a == undefined などで判定するとかにしてください。 (多分、他にもあると思うけれど、とりあえず)
お礼
細かい例外処理についてはなんとか自分でやってみます! ありがとうございます!!
- babu_baboo
- ベストアンサー率51% (268/525)
No5と3です。 たぶん canvas でもいけるとおもっているひとりです。 しかし、そのえんぐらふのぷれぜんほうほうが、いけてないとおもいます。 そもそもえんぐらふは、にかよったあたいをはんべつしにくい。 そのうえ、たかさ(はんけい)のちがいでも、2じてきなすうちをひょうげんしようとしています。 それなのに、まうすがかさなると、おおきくなるのでは、その2じてきすうちがいみをうしなってしまいます。
お礼
ご指摘ありがとうございます。 確かにご指摘の通り、この円グラフにこのアニメーションでは意味が薄れてしまいますね。。。 もう一度UIも考えながら作業を進めたいと思います。 babu_babooさんも最後までアドバイス頂きありがとうございました!
- fujillin
- ベストアンサー率61% (1594/2576)
#3です。 >知識をご教授頂けないでしょうか? もともと知識がないので、canvasも自分のお勉強のために調べて書いてみた次第。 アニメーションはcanvasでも可能は可能ですが、全体を消して再度書き直すというのが基本みたいです。 それなので、画像そのものをオブジェクトっぽくは扱えない。 多少なりとも似せるなら、各画像を個別のcanvasに描いておいて重ねて表示をすれば、移動(もしかすると拡大・縮小も?←調べていません)程度ならば再描画しなくても可能になるかと思います。 いずれにしても、マウスの位置から画像を特定する部分は作成しないとならないでしょうから、そのあたりが面倒そうですね。 その点、#1様、#4様はさすがに読みが深く、SVGなら描画画像がオブジェクトになっているのでご希望のようなものには適しているかと。 私は、SVGも知らないのでハードルが高いけれど、またまた、にわか勉強で… とは思ったものの、さすがにドキュメントが多くて、素から作成するには短時間ではとても無理そう。 てーことで、質問者様が御紹介のraphaelに走ってみました。 基本的には、#1様、#4様がご指摘のサイトにサンプルがあるので、半分コピペみたいなものですが… それなので、詳しく知りたい時は御両者が提示なさっているサイトあたりを参照なさるのがよろしいかと。 <参考サイト> http://www.w3.org/TR/2003/REC-SVG11-20030114/paths.html (pathのあたりを超斜め読みだけなので、↓は相当にいい加減。きちんと直してください。) <!DOCTYPE html> <html lang="ja"> <head> <title>sample</title> <script type="text/javascript" src="js/raphael/raphael.js"></script> </head> <body> <div id="holder" style="width:400px; height:400px;"></div> <script type="text/javascript"> (function(){ // 角度変換関数 var radian = function(a){ return ((a-90) % 360) * Math.PI / 180; } // アニメーション定義用関数 var move = function(e, a){ a = a?"t" + 15*Math.cos(a) + " " + 15*Math.sin(a) : ""; return function(){ e.stop().animate({transform:a}, 100); } } // 扇形描画+アニメーション設定 var setFan = function(r, ang1, ang2, color1, color2){ var x = center.x, y = center.y; var LAflag = (ang2 - ang1) > 180; var ang = radian((ang1 + ang2) / 2); ang1 = radian(ang1); ang2 = radian(ang2); var f = paper.path(["M", x, y, "l", r*Math.cos(ang1), r*Math.sin(ang1), "A", r, r, 0, +LAflag, 1, x+r*Math.cos(ang2), y+r*Math.sin(ang2), "Z"]); f.attr({fill:color1, stroke:color2}).hover(move(f, ang), move(f)); } // 初期設定 var holder = document.getElementById("holder"); var center = { x: parseInt(holder.clientWidth)/2 | 0, y: parseInt(holder.clientHeight)/2 | 0 } var paper = Raphael(holder, center.x*2, center.y*2); paper.circle(center.x, center.y, 200).attr({fill:"#ddd", stroke:"#ddd"}); setFan(180, 0, 10, "#0c0", "#060"); setFan(140, 10, 240, "#c00", "#600"); setFan(100, 240, 360, "#00c", "#006"); })(); </script> </body> </html>
お礼
ご丁寧にありがとうございます。 現在は開発に費用をかけられない関係で1から勉強するのは正直厳しい状況です。 このコードをベースに改良を加えていきたいと思います。 本当にご丁寧にありがとうございました!
- dscripty
- ベストアンサー率51% (166/325)
Raphael.js を出してきたからあやしいとおもっけど、やっぱりアニメーションかぁ 「ひとつひとつの扇形をオフジェクトとして取り出して、クリックしたらajaxを使って該当データを引っ張ってきて、ツールチップに表示、こうしたこと」は canvas は得意じゃないから、やっぱり SVG だとおもうよ? と推してみる。
お礼
ご返事ありがとうございます。 アニメーション難しいですね。。 初めてのコンタクトなのに大変恐縮なんですが、 http://elycharts.com/examples#example_p1 のようなアニメーション付きの、しかも今回のようなちょっと特殊な円グラフの、動くコードをなんとか頂けないでしょうか?もしくは作成の手順を、教えて頂けないでしょうか? 本当に簡素なもので大丈夫ですので、知識をご教授下さい。
- babu_baboo
- ベストアンサー率51% (268/525)
さらに、あにめーしょん!? あとだしじゃんけんのようなものには・・・ きっと、Fujillin さんならやるっ!とふってみる。(きっとだいすきだろうから^^;) ところで、えんぐらふの value でぐらふのかくどがへんかするのだけれど、 radius のきじゅんはなに? さいしゅうてきなもくてきをたっせいするために、うごくこーどがほしいの? てじゅんだとか、かんがえかた、あるごりずむだとかを、まなびたいの?
お礼
ご返事ありがとうございます。 説明が足らず申し訳ありませんでした。。 半径は、90%~100%の人,80%~89%の人,70%~79%の人,60%~69%の人,50%~59%の人,40%~49%の人,30%~39%の人,20%~29%の人,10%~19%の人,0%~9%の人という10個の半径を取ります。なので順に200px,180px,160px,140px,120px,100px,80px,60px,40px,20pxという値を半径にしようと思っています。 質問の量が多すぎるので、ある程度は手順だけでも仕方ないかともおもっていますが できれば、先ほど載せたサイトの円グラフのようなアニメーションのついた動くコードを頂けたら幸いです。。
- 神崎 渉瑠(@taloo)
- ベストアンサー率44% (1016/2280)
No.1と同じですが。 http://raphaeljs.com/pie.html こちらのサンプルでは、 pie.jsより paper.path(["M", cx, cy, "L", x1, y1, "A", r, r, 0, +(endAngle - startAngle > 180), 0, x2, y2, "z"]).attr(params); としていますね。 paramsは色情報が入っているようです。 http://raphaeljs.com/growing-pie.html こちらのサンプルではRaphaelを拡張していますが return { path: [["M", x, y], ["l", r * Math.cos(a1), r * Math.sin(a1)], ["A", r, r, 0, +flag, 1, x + r * Math.cos(a2), y + r * Math.sin(a2)], ["z"]], fill: "hsb(" + clr + ", .75, .8)" }; となっていますので、円グラフの基本はpathを使うようです。 一番外側の、灰色の円はPaper.circle()でいいと思います。
お礼
ご回答ありがとうございます。 jsは得意ではないので、時間をかけてリファレンスの方参考にして勉強してみます!! まさにこうしたアニメーションをグラフにつけたいんですよね!みなさんのご意見を参考に考えてみます。
- 1
- 2
お礼
ご回答ありがとうございます。正常に動きました! コードも非常に簡潔でしたので、取り入れてみたいと思ってます!ちなみにNo2さんのほうにも記載させて頂きましたが、 http://elycharts.com/examples#example_p1 こうしたサンプルのように、ひとつひとつの扇形に対してアニメーションもつけたいんです。。 また、ひとつひとつの扇形をオフジェクトとして取り出して、クリックしたらajaxを使って該当データを引っ張ってきて、ツールチップに表示、こうしたことをやっていきたいとおもっているんですね。 どうかfujillin様からも知識をご教授頂けないでしょうか?