d3.js甜甜圈饼图传奇翻转

时间:2014-10-27 12:15:07

标签: javascript d3.js

我正在开发一个图例切换d3.js饼图应用程序,使用此jsfiddle作为我的最新版本http://jsfiddle.net/Qh9X5/3328/

我的目标是获得一个简化的工作示例,其中图例可以切换切片,尝试停用所有切片 - 重置中的重新调整会重新激活所有切片。拆分演示文稿和应用层逻辑。

补间也需要改进 - 随着切片的出现,然后顺利地重新补间。

如何改进/修复此代码库中的各种错误?

onLegendClick: function(dt, i){
                    //_toggle rectangle in legend

var completeData = jQuery.extend(true, [], methods.currentDataSet);




                    newDataSet = completeData;                        
                    if(methods.manipulatedData){
                        newDataSet = methods.manipulatedData;
                    }


                    d3.selectAll('rect')
                    .data([dt], function(d) { 
                        return d.data.label;
                    })
                    .style("fill-opacity", function(d, j) {
                        var isActive = Math.abs(1-d3.select(this).style("fill-opacity"));
                        if(isActive){
                            newDataSet[j].total = completeData[j].total;                                
                        }else{
                            newDataSet[j].total = 0;                            
                        }

                        return isActive;
                    });                            

                    //animate slices
                    methods.animateSlices(newDataSet);

                    //stash manipulated data
                    methods.manipulatedData = newDataSet;

                }

1 个答案:

答案 0 :(得分:0)

这是整个js代码 - 我已经使用了tidyup。我不确定使用快捷方式,因为我不确定这些值是否正确。最新的小提琴 - http://jsfiddle.net/Qh9X5/3340/

$(document).ready(function () {

    var pieChart = {
        el: "",
        init: function (el, options) {
            var clone = jQuery.extend(true, {}, options["data"]);

            pieChart.el = el;
            pieChart.setup(clone, options["width"], options["height"], options["r"], options["ir"]);
        },
        getArc: function (radius, innerradius) {
            var arc = d3.svg.arc()
                .innerRadius(innerradius)
                .outerRadius(radius);

            return arc;
        },
        setup: function (dataset, w, h, r, ir) {

            var padding = 80;

            this.width = w;
            this.height = h;
            this.radius = r
            this.innerradius = ir;

            this.color = d3.scale.category20();

            this.pie = d3.layout.pie()
                .sort(null)
                .value(function (d) {
                return d.total;
            });

            this.arc = this.getArc(this.radius, this.innerradius);

            this.svg = d3.select(pieChart.el["selector"]).append("svg")
                .attr("width", this.width + padding)
                .attr("height", this.height + padding);

            this.holder = this.svg.append("g")
                .attr("transform", "translate(" + ((this.width / 2) + (padding / 2)) + "," + ((this.height / 2) + (padding / 2)) + ")");

            this.piec = this.holder.append("g")
                .attr("class", "piechart");

            this.segments = this.holder.append("g")
                .attr("class", "segments");

            this.labels = this.holder.append("g")
                .attr("class", "labels");

            this.pointers = this.holder.append("g")
                .attr("class", "pointers");

            this.legend = this.svg.append("g")
                .attr("class", "legend")
                .attr("transform", "translate(" + -(this.width / 4) + "," + this.height + ")");

        },
        oldPieData: "",
        pieTween: function (r, ir, d, i) {
            var that = this;

            var theOldDataInPie = pieChart.oldPieData;
            // Interpolate the arcs in data space

            var s0;
            var e0;

            if (theOldDataInPie[i]) {
                s0 = theOldDataInPie[i].startAngle;
                e0 = theOldDataInPie[i].endAngle;
            } else if (!(theOldDataInPie[i]) && theOldDataInPie[i - 1]) {
                s0 = theOldDataInPie[i - 1].endAngle;
                e0 = theOldDataInPie[i - 1].endAngle;
            } else if (!(theOldDataInPie[i - 1]) && theOldDataInPie.length > 0) {
                s0 = theOldDataInPie[theOldDataInPie.length - 1].endAngle;
                e0 = theOldDataInPie[theOldDataInPie.length - 1].endAngle;
            } else {
                s0 = 0;
                e0 = 0;
            }

            var i = d3.interpolate({
                startAngle: s0,
                endAngle: e0
            }, {
                startAngle: d.startAngle,
                endAngle: d.endAngle
            });

            return function (t) {
                var b = i(t);
                return pieChart.getArc(r, ir)(b);
            };
        },
        removePieTween: function (r, ir, d, i) {
            var that = this;
            s0 = 2 * Math.PI;
            e0 = 2 * Math.PI;
            var i = d3.interpolate({
                startAngle: d.startAngle,
                endAngle: d.endAngle
            }, {
                startAngle: s0,
                endAngle: e0
            });

            return function (t) {
                var b = i(t);
                return pieChart.getArc(r, ir)(b);
            };
        },
        animateSlices: function (dataSet) {

            var r = $(pieChart.el["selector"]).data("r");
            var ir = $(pieChart.el["selector"]).data("ir");

            this.piedata = pieChart.pie(dataSet);

            //__slices
            this.path = pieChart.segments.selectAll("path.pie")
                .data(this.piedata, function (d) {
                return d.data.label
            });

            this.path.enter().append("path")
                .attr("class", "pie")
                .attr("fill", function (d, i) {
                return pieChart.color(i);
            })
                .attr("stroke", "#ffffff")
                .transition()
                .duration(300)
                .attrTween("d", function (d, i) {
                return pieChart.pieTween(r, ir, d, i);
            });

            this.path.transition()
                .duration(300)
                .attrTween("d", function (d, i) {
                return pieChart.pieTween(r, ir, d, i);
            });

            this.path.exit()
                .transition()
                .duration(300)
                .attrTween("d", function (d, i) {
                return pieChart.removePieTween(r, ir, d, i);
            })
                .remove();
            //__slices

            //__labels  
            var labels = pieChart.labels.selectAll("text")
                .data(this.piedata, function (d) {
                return d.data.label
            });

            labels.enter()
                .append("text")
                .attr("text-anchor", "middle")

            labels.attr("x", function (d) {
                var a = d.startAngle + (d.endAngle - d.startAngle) / 2 - Math.PI / 2;
                d.cx = Math.cos(a) * (ir + ((r - ir) / 2));
                return d.x = Math.cos(a) * (r + 20);
            })
                .attr("y", function (d) {
                var a = d.startAngle + (d.endAngle - d.startAngle) / 2 - Math.PI / 2;
                d.cy = Math.sin(a) * (ir + ((r - ir) / 2));
                return d.y = Math.sin(a) * (r + 20);
            })
                .attr("opacity", function (d) {
                var opacityLevel = 1;
                if (d.value == 0) {
                    opacityLevel = 0;
                }
                return opacityLevel;
            })
                .text(function (d) {
                return d.data.label;
            })
                .each(function (d) {
                var bbox = this.getBBox();
                d.sx = d.x - bbox.width / 2 - 2;
                d.ox = d.x + bbox.width / 2 + 2;
                d.sy = d.oy = d.y + 5;
            })
                .transition()
                .duration(300)

            labels.transition()
                .duration(300)

            labels.exit().remove();
            //__labels

            //__pointers
            pieChart.pointers.append("defs").append("marker")
                .attr("id", "circ")
                .attr("markerWidth", 6)
                .attr("markerHeight", 6)
                .attr("refX", 3)
                .attr("refY", 3)
                .append("circle")
                .attr("cx", 3)
                .attr("cy", 3)
                .attr("r", 3);

            var pointers = pieChart.pointers.selectAll("path.pointer")
                .data(this.piedata, function (d) {
                return d.data.label
            });

            pointers.enter()
                .append("path")
                .attr("class", "pointer")
                .style("fill", "none")
                .style("stroke", "black")
                .attr("marker-end", "url(#circ)");

            pointers.attr("d", function (d) {
                if (d.cx > d.ox) {
                    return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
                } else {
                    return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
                }
            })
                .attr("opacity", function (d) {
                var opacityLevel = 1;
                if (d.value == 0) {
                    opacityLevel = 0;
                }
                return opacityLevel;
            })
                .transition()
                .duration(300)

            pointers.transition()
                .duration(300)

            pointers.exit().remove();
        },
        onToggle: function (sliceData, index) {
            //_toggle rectangle in legend

            //_toggle slice
            var completeData = jQuery.extend(true, [], pieChart.currentDataSet);
            var dataLength = completeData.length;

            var newDataSet = completeData;
            if (pieChart.manipulatedData) {
                newDataSet = pieChart.manipulatedData;
            }

            d3.selectAll('rect')
                .data([sliceData], function (d) {
                return d.data.label;
            })
                .style("fill-opacity", function (d) {
                var isActive = Math.abs(1 - d3.select(this).style("fill-opacity"));
                if (isActive) {
                    newDataSet[index].total = completeData[index].total;
                    newDataSet[index].value = completeData[index].value;
                } else {
                    newDataSet[index].total = 0;
                    newDataSet[index].value = 0;
                }

                return isActive;
            });

            //if all elements are to be not shown - reset to show all slices again.
            //animate slices
            pieChart.animateSlices(newDataSet);

            //stash manipulated data
            pieChart.manipulatedData = newDataSet;
        },
        update: function (el, dataSet) {
            var that = this;

            pieChart.el = el;

            pieChart.svg = d3.select(pieChart.el["selector"] + " .piechart");

            pieChart.segments = d3.select(pieChart.el["selector"] + " .segments");
            pieChart.labels = d3.select(pieChart.el["selector"] + " .labels");
            pieChart.pointers = d3.select(pieChart.el["selector"] + " .pointers");

            pieChart.legend = d3.select(pieChart.el["selector"] + " .legend");

            dataSet.forEach(function (d) {
                d.total = +d.value;
            });

            pieChart.currentDataSet = dataSet;
            pieChart.animateSlices(dataSet);

            //__legends
            var w = 200;

            // add legend   
            var legend = pieChart.legend; //.append("g")

            var legendRects = legend.selectAll('rect')
                .data(this.piedata, function (d) {
                return d.data.label
            });

            legendRects.enter()
                .append("rect")
                .attr("x", w - 65)
                .attr("y", function (d, i) {
                return i * 20;
            })
                .attr("width", 10)
                .attr("height", 10)
                .style("fill", function (d, i) {
                return pieChart.color(i);
            })
                .style("stroke", function (d, i) {
                return pieChart.color(i);
            })
            .on('click', function(d, i){
                pieChart.onToggle(d, i);
            })
                .transition()
                .duration(300)

            legendRects.style("fill", function (d, i) {
                return pieChart.color(i);
            })
                .style("stroke", function (d, i) {
                return pieChart.color(i);
            })
                .transition()
                .duration(300)

            legendRects.exit().remove();

            var legendText = legend.selectAll('text.label')
                .data(this.piedata, function (d) {
                return d.data.label
            });

            legendText.enter()
                .append("text")
                .attr("class", "label")
                .attr("x", w - 52)
                .attr("y", function (d, i) {
                return i * 20 + 9;
            })
                .text(function (d) {
                return d.data.label;
            })
                .transition()
                .duration(300)

            legendText.text(function (d) {
                return d.data.label;
            })
                .transition()
                .duration(300)

            legendText.exit().remove();

            var legendTextVals = legend.selectAll('text.vals')
                .data(this.piedata, function (d) {
                return d.data.label
            });

            legendTextVals.enter()
                .append("text")
                .attr("class", "vals")
                .attr("x", w + 20)
                .attr("y", function (d, i) {
                return i * 20 + 9;
            })
                .text(function (d) {
                return d.data.value;
            })
                .transition()
                .duration(300)

            legendTextVals.text(function (d) {
                return d.data.value;
            })
                .transition()
                .duration(300)

            legendTextVals.exit().remove();

            //__pointers

            this.oldPieData = this.piedata;
        }
    };

    var dataCharts = [{
        "data": [{
            "segments": [{
                "label": "apple",
                    "value": 53245
            }, {
                "label": "cherry",
                    "value": 145
            }, {
                "label": "pear",
                    "value": 2245
            }, {
                "label": "bananana",
                    "value": 15325
            }]
        }]
    }, {
        "data": [{
            "segments": [{
                "label": "milk",
                    "value": 122
            }, {
                "label": "cheese",
                    "value": 44
            }, {
                "label": "grapes",
                    "value": 533
            }]
        }]
    }, {
        "data": [{
            "segments": [{
                "label": "pineapple",
                    "value": 1532
            }, {
                "label": "orange",
                    "value": 1435
            }, {
                "label": "grapes",
                    "value": 22
            }]
        }]
    }, {
        "data": [{
            "segments": [{
                "label": "lemons",
                    "value": 133
            }, {
                "label": "mango",
                    "value": 435
            }, {
                "label": "melon",
                    "value": 2122
            }]
        }]
    }];

    var clone = jQuery.extend(true, {}, dataCharts);

    //__invoke concentric
    $('[data-role="piechart"]').each(function (index) {
        var selector = "piechart" + index;

        $(this).attr("id", selector);

        var options = {
            data: clone[0].data,
            width: $(this).data("width"),
            height: $(this).data("height"),
            r: $(this).data("r"),
            ir: $(this).data("ir")
        }

        pieChart.init($("#" + selector), options);
        pieChart.update($("#" + selector), clone[0].data[0].segments);
    });

    $(".testers a").on("click", function (e) {
        e.preventDefault();

        var clone = jQuery.extend(true, {}, dataCharts);

        var min = 0;
        var max = 3;

        //__invoke pie chart
        $('[data-role="piechart"]').each(function (index) {
            pos = Math.floor(Math.random() * (max - min + 1)) + min;
            pieChart.update($("#" + $(this).attr("id")), clone[pos].data[0].segments);
        });
    });
});