diff --git a/lib/charts/pie.js b/lib/charts/pie.js index 2bd7200..d3b8abc 100644 --- a/lib/charts/pie.js +++ b/lib/charts/pie.js @@ -15,11 +15,21 @@ /** * 构造函数 - * @param {Object} node 表示在html的哪个容器中绘制该组件 - * @param {Object} options 为用户自定义的组件的属性,比如画布大小 * Options: - * - `width` 图宽度 - * - `height` 图高度 + * + * - `width` 图片宽度,默认为800,表示图片高800px + * - `height` 图片高度,默认为800 + * - `legend` 图例是否显示,默认为 true, 显示;设为false则不显示 + * - `ms` 动画持续时间,默认300 + * - `easing` 动画类型,默认“bounce”。详见rapheal相关文档,可以使用“linear”,“easeIn”,“easeOut”,“easeInOut”,“backIn”,“backOut”,“elastic”,“bounce” + * + * Examples: + * create Radar Chart in a dom node with id "chart", width is 500; height is 600px; + * ``` + * var radar = new Radar("chart", {"width": 500, "height": 600}); + * ``` + * @param {Object} container 表示在html的哪个容器中绘制该组件 + * @param {Object} options 为用户自定义的组件的属性,比如画布大小 */ var Pie = DataV.extend(DataV.Chart, { type: "Pie", @@ -53,6 +63,8 @@ this.defaults.legend = true; this.defaults.width = 800; this.defaults.height = 800; + this.defaults.ms = 300; + this.defaults.easing = "bounce"; //设置用户指定的属性 this.setOptions(options); @@ -98,16 +110,16 @@ * 绘制饼图 */ Pie.prototype.render = function () { + this.layout(); var conf = this.defaults; var floatTag = this.floatTag; var that = this; - this.layout(); var groups = this.groups; //由内外半径、起始角度计算路径字符串 var pathCalc = d3.svg.arc() .innerRadius(conf.radius) - .outerRadius(0) + .outerRadius(conf.radius * 0.2) .startAngle(function (d) { return d.startAngle; }).endAngle(function (d) { @@ -121,6 +133,7 @@ that.donutGroups = that.canvas.set(); $(this.node).append(this.floatTag); + //添加透明效果 var mouseOver = function () { floatTag.html('
' + this.data('text') + '
'); @@ -155,17 +168,34 @@ d.attr('fill-opacity', 1); }); } else if (!this.data('click')) { + this.attr('fill-opacity', 0.5); } }; var mouseClick = function () { var index = this.data("donutIndex"); + var fan = groups[index]; var flag = !this.data('click'); this.data('click', flag); - var a = 0.5 * ((that.groups[index].startAngle + that.groups[index].endAngle) - Math.PI); - var nameX = conf.protrude * Math.cos(a); - var nameY = conf.protrude * Math.sin(a); + var ro = (fan.startAngle + fan.endAngle) * 90 / Math.PI; + var angle = 0.5 * ((fan.startAngle + fan.endAngle) - Math.PI); + var center = { + x: ((conf.width - that.xOffset) / 2 + that.xOffset), + y: conf.height / 2 + }; + var namePos = { + x: conf.protrude * Math.cos(angle), + y: conf.protrude * Math.sin(angle) + }; + var radius = { + x: conf.radius * Math.cos(angle) + namePos.x, + y: conf.radius * Math.sin(angle) + namePos.y + }; + var linePos = { + x: 9 * Math.cos(angle), + y: 9 * Math.sin(angle) + }; if (flag) { if (that.click === 0) { that.donutGroups.forEach(function (d) { @@ -176,14 +206,28 @@ } that.underBn[index].attr('opacity', 1).show(); this.attr('fill-opacity', 1); - this.data('nameTag').translate(0, - conf.protrude); - this.data('line').translate(0, - conf.protrude); - this.translate(nameX, nameY); + this.data('nameTag').stop().animate({ + transform: "t" + (center.x + radius.x + 2 * namePos.x) + " " + (center.y + radius.y + 2 * namePos.y) + "r" + ro + }, conf.ms, conf.easing); + this.data('line').stop().animate({ + transform: "t" + (center.x + radius.x + namePos.x - linePos.x) + " " + (center.y + radius.y + namePos.y - linePos.y) + "r" + ro + }, conf.ms, conf.easing); + this.stop().animate({ + transform: "t" + (center.x + namePos.x) + " " + (center.y + namePos.y) + }, conf.ms, conf.easing); + //this.translate(nameX, nameY); that.click += 1; } else { - this.data('nameTag').translate(0, conf.protrude); - this.data('line').translate(0, conf.protrude); - this.translate(-nameX, - nameY); + this.data('nameTag').stop().animate({ + transform: "t" + (center.x + radius.x + namePos.x) + " " + (center.y + radius.y + namePos.y) + "r" + ro + }, conf.ms, conf.easing); + this.data('line').stop().animate({ + transform: "t" + (center.x + radius.x - linePos.x) + " " + (center.y + radius.y - linePos.y) + "r" + ro + }, conf.ms, conf.easing); + this.stop().animate({ + transform: "t" + center.x + " " + center.y + }, conf.ms, conf.easing); + //this.translate(-nameX, - nameY); that.click -= 1; if (that.click > 0) { this.attr('fill-opacity', 0.5); @@ -195,12 +239,13 @@ var i; var nameStr; var nameX, nameY; - var ro, a; + var ro, angle; for (i = 0; i <= groups.length - 1; i++) { + var fan = groups[i]; //画外圈的pie图 //计算每个group的path - spline = pathCalc(groups[i]); - tips = that.groupNames[i] + ": " + Math.round(groups[i].value) + " " + (groups[i].value * 100 / this.sum).toFixed(2) + "%"; + spline = pathCalc(fan); + tips = fan.nameTag + ": " + Math.round(fan.value) + " " + (fan.value * 100 / this.sum).toFixed(2) + "%"; donutEle = that.canvas.path(spline) .translate((conf.width - this.xOffset) / 2 + this.xOffset, conf.height / 2) @@ -215,15 +260,17 @@ .click(mouseClick); //每个donut上显示名称 - ro = (groups[i].startAngle + groups[i].endAngle) * 90 / Math.PI; - a = 0.5 * ((groups[i].startAngle + groups[i].endAngle) - Math.PI); - nameX = (conf.radius + 2 * conf.protrude) * Math.cos(a); - nameY = (conf.radius + 2 * conf.protrude) * Math.sin(a); + ro = (fan.startAngle + fan.endAngle) * 90 / Math.PI; + angle = 0.5 * ((fan.startAngle + fan.endAngle) - Math.PI); + nameX = (conf.radius + 2 * conf.protrude) * Math.cos(angle); + nameY = (conf.radius + 2 * conf.protrude) * Math.sin(angle); nameStr = "T" + ((conf.width - that.xOffset) / 2 + that.xOffset) + "," + conf.height / 2 + "R" + ro + "T" + nameX + "," + nameY; var line = that.canvas.path("M,0,-" + conf.protrude + "L0," + conf.protrude).transform(nameStr).translate(0, conf.protrude + 9); - var nameTag = that.canvas.text().attr("font", "18px Verdana").attr("text", that.groupNames[i]).transform(nameStr); - + var nameTag = that.canvas.text().attr({ + "font": "18px Verdana", + "text": fan.nameTag + }).translate(((conf.width - that.xOffset) / 2 + that.xOffset) + nameX, conf.height / 2 + nameY).rotate(ro); //transform(nameStr); donutEle.data('text', tips).data('click', false).data('nameTag', nameTag).data('line', line); that.donutGroups.push(donutEle); } @@ -240,6 +287,7 @@ var that = this; var conf = this.defaults; var paper = this.canvas; + var groups = this.groups; var legendArea = this.legendArea; this.rectBn = paper.set(); var rectBn = this.rectBn; @@ -250,6 +298,7 @@ underBn.push(paper.rect(legendArea[0] + 10, legendArea[1] + 10 + (20 + 3) * i, 180, 20).attr({ "fill": "#ebebeb", "stroke": "none" + //"r": 3 }).hide()); //色框 paper.rect(legendArea[0] + 10 + 3, legendArea[1] + 10 + (20 + 3) * i + 6, 16, 8).attr({ @@ -257,7 +306,7 @@ "stroke": "none" }); //文字 - paper.text(legendArea[0] + 10 + 3 + 16 + 8, legendArea[1] + 10 + (20 + 3) * i + 10, this.groupNames[i]).attr({ + paper.text(legendArea[0] + 10 + 3 + 16 + 8, legendArea[1] + 10 + (20 + 3) * i + 10, this.groups[i].nameTag).attr({ "fill": "black", "fill-opacity": 1, "font-family": "Verdana", @@ -269,10 +318,12 @@ "fill": "white", "fill-opacity": 0, "stroke": "none" + //"r": 3 })); } rectBn.forEach(function (d, i) { // TODO: 这里的事件建议采用事件委托 + var fan = groups[i]; d.mouseover(function () { if (!that.donutGroups[i].data("click")) { underBn[i].attr('opacity', 0.5).show(); @@ -283,9 +334,24 @@ } }); d.click(function () { - var a = 0.5 * ((that.groups[i].startAngle + that.groups[i].endAngle) - Math.PI); - var nameX = conf.protrude * Math.cos(a); - var nameY = conf.protrude * Math.sin(a); + var ro = (fan.startAngle + fan.endAngle) * 90 / Math.PI; + var angle = 0.5 * ((fan.startAngle + fan.endAngle) - Math.PI); + var center = { + x: ((conf.width - that.xOffset) / 2 + that.xOffset), + y: conf.height / 2 + }; + var namePos = { + x: conf.protrude * Math.cos(angle), + y: conf.protrude * Math.sin(angle) + }; + var radius = { + x: conf.radius * Math.cos(angle) + namePos.x, + y: conf.radius * Math.sin(angle) + namePos.y + }; + var linePos = { + x: 9 * Math.cos(angle), + y: 9 * Math.sin(angle) + }; if (!that.donutGroups[i].data("click")) { if (that.click === 0) { that.donutGroups.forEach(function (d) { @@ -296,14 +362,27 @@ } underBn[i].attr('opacity', 1).show(); that.donutGroups[i].data("click", true).attr('fill-opacity', 1); - that.donutGroups[i].data('nameTag').translate(0, - conf.protrude); - that.donutGroups[i].data('line').translate(0, - conf.protrude); - that.donutGroups[i].translate(nameX, nameY); + that.donutGroups[i].data('nameTag').stop().animate({ + transform: "t" + (center.x + radius.x + 2 * namePos.x) + " " + (center.y + radius.y + 2 * namePos.y) + "r" + ro + }, conf.ms, conf.easing); + that.donutGroups[i].data('line').stop().animate({ + transform: "t" + (center.x + radius.x + namePos.x - linePos.x) + " " + (center.y + radius.y + namePos.y - linePos.y) + "r" + ro + }, conf.ms, conf.easing); + that.donutGroups[i].stop().animate({ + transform: "t" + (center.x + namePos.x) + " " + (center.y + namePos.y) + }, conf.ms, conf.easing); that.click += 1; + } else if (that.donutGroups[i].data("click")) { - that.donutGroups[i].data('nameTag').translate(0, conf.protrude); - that.donutGroups[i].data('line').translate(0, conf.protrude); - that.donutGroups[i].translate(-nameX, - nameY); + that.donutGroups[i].data('nameTag').stop().animate({ + transform: "t" + (center.x + radius.x + namePos.x) + " " + (center.y + radius.y + namePos.y) + "r" + ro + }, conf.ms, conf.easing); + that.donutGroups[i].data('line').stop().animate({ + transform: "t" + (center.x + radius.x - linePos.x) + " " + (center.y + radius.y - linePos.y) + "r" + ro + }, conf.ms, conf.easing); + that.donutGroups[i].stop().animate({ + transform: "t" + center.x + " " + center.y + }, conf.ms, conf.easing); that.click -= 1; if (that.click > 0) { that.donutGroups[i].attr('fill-opacity', 0.5); @@ -314,6 +393,7 @@ } underBn[i].hide(); that.donutGroups[i].data("click", false); + } }); }); @@ -336,22 +416,28 @@ */ Pie.prototype.layout = function () { var that = this; + that.canvas.clear(); - var acc = 0; + this.sum = DataV.sum(this.groupValue); var sum = this.sum; this.groups = this.groupValue.map(function (item, index) { - var startAngle = 2 * acc * Math.PI / sum; - acc += item; - var endAngle = 2 * acc * Math.PI / sum; var ret = { index: index, value: item, - startAngle: startAngle, - endAngle: endAngle + nameTag: that.groupNames[index] }; return ret; }); + this.groups = _.sortBy(that.groups, function (d) { + return -d.value; + }); + var acc = 0; + this.groups.forEach(function (d) { + d.startAngle = 2 * acc * Math.PI / sum; + acc += d.value; + d.endAngle = 2 * acc * Math.PI / sum; + }); }; return Pie;