1

我是 javascript 和 D3 的新手。

我正在尝试创建一个堆叠面积图,在以绝对比例和百分比比例显示数据之间切换。我意识到这涉及玩弄

d3.layout.stack().offset("zero") 和 d3.layout.stack().offset("expand")。

我已经成功地让图表做我想做的事:

http://jsfiddle.net/dSJ4E/

...但我并不为我的方法感到自豪,并且确信有更好的方法来做到这一点。有任何想法吗?有没有你可能知道的简单例子?

我对我的代码不满意,因为在设置 if/else 语句时,我只是重新声明了之前编写的所有内容,只更改了偏移变量。这似乎是一种笨拙的方法。另外,我认为这不会让我设置过渡。

data = [{"type": "Group1",
             "values": [
                {"x":0, "y": 2.5},
                {"x":1, "y": 2.4},
                {"x":2, "y": 0.3}]},
            {"type": "Group2",
             "values": [
                {"x":0, "y": 1.5},
                {"x":1, "y": 1.3},
                {"x":2, "y": 1.1}]}
            ];
var stackZero = d3.layout.stack()
        .values(function(d){return d.values;})  
        .offset("zero");

stackZero(data);
var xScale = d3.scale.linear()
        .domain([0,2])
        .range([0, width]);

    var yScale = d3.scale.linear()
        .range([height,0])
        .domain([0, d3.max(data, function(d){return d3.max(d.values, function(d){return d.y0 + d.y;});})]);

    var area = d3.svg.area()
        .x(function(d){return xScale(d.x);})
        .y0(function(d){return yScale(d.y0);})
        .y1(function(d){return yScale(d.y0 + d.y);});       

    var svg = d3.selectAll("body")
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top +")");

    svg.selectAll(".layers")
        .data(data)
        .enter()
        .append("path")
        .attr("class", "layer")
        .attr("d", function(d){return area(d.values);})
        .style("fill", function (d,i){return colors(i)});

    var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient("left");

    svg.append("g")
        .attr("class", "y axis")
        .attr("transform", "translate(0,0)")
        .call(yAxis);   

    d3.select("p") //now we start to interact with the chart
        .on("click", function() { 

            console.log("entering variable is " + stackType);

            svg.selectAll("path").data([]).exit().remove();
            svg.selectAll(".y.axis").data([]).exit().remove();
if(stackType){ //enter true, or expanded data

            var stackExpand = d3.layout.stack()
                .values(function(d){return d.values;})
                .offset("expand");

            stackExpand(data);

            console.log(data);

            var yScale = d3.scale.linear()
                .range([height,0])
                .domain([0, d3.max(data, function(d){return d3.max(d.values, function(d){return d.y0 + d.y;});})]);

            var area = d3.svg.area()
                .x(function(d){return xScale(d.x);})
                .y0(function(d){return yScale(d.y0);})
                .y1(function(d){return yScale(d.y0 + d.y);});

            svg.selectAll(".layers")
                //.data(stackZero(data))
                .data(stackExpand(data))
                .enter()
                .append("path")
                .attr("class", "layer")
                .attr("d", function(d){return area(d.values);})
                .style("fill", function (d,i){return colors(i)});

            formatter = d3.format(".0%");

            var yAxis = d3.svg.axis()
                .scale(yScale)
                .orient("left")
                .tickFormat(formatter);

            svg.append("g")
                .attr("class", "y axis")
                .attr("transform", "translate(0,0)")
                .call(yAxis);


            stackType = false;
            console.log("exiting variable is " + stackType);

            } else { //enter false, or zero data

            data = [{"type": "Group1",
             "values": [
                {"x":0, "y": 2.5},
                {"x":1, "y": 2.4},
                {"x":2, "y": 0.3}]},
            {"type": "Group2",
             "values": [
                {"x":0, "y": 1.5},
                {"x":1, "y": 1.3},
                {"x":2, "y": 1.1}]}
            ];

            var stackZero = d3.layout.stack()
                .values(function(d){return d.values;})
                .offset("zero");

            stackZero(data);

            console.log(data);

            var yScale = d3.scale.linear()
                .range([height,0])
                .domain([0, d3.max(data, function(d){return d3.max(d.values, function(d){return d.y0 + d.y;});})]);

            var area = d3.svg.area()
                .x(function(d){return xScale(d.x);})
                .y0(function(d){return yScale(d.y0);})
                .y1(function(d){return yScale(d.y0 + d.y);});

            svg.selectAll(".layers")
                .data(stackZero(data))
                //.data(stackExpand(data))
                .enter()
                .append("path")
                .attr("class", "layer")
                .attr("d", function(d){return area(d.values);})
                .style("fill", function (d,i){return colors(i)});   

            stackType = true;
            console.log("exiting variable is" + stackType);

            var yAxis = d3.svg.axis()
                .scale(yScale)
                .orient("left");

            svg.append("g")
                .attr("class", "y axis")
                .attr("transform", "translate(0,0)")
                .call(yAxis);

            };  

});
    </script>
4

1 回答 1

1

因此,您可以做很多事情来避免在代码中重复自己。首先,您正在使用的许多 D3 函数都可以重用——它们是通用函数。这与 javascript 闭包的概念一起意味着您只需声明它们一次并初始化不会更改的部分。

堆栈布局确实覆盖了 y 和 y0 值,因此为了使切换正常工作,我将您的初始数据重命名为 raw_x 和 raw_y 并适当地调整了访问器。

然后找出 yScale 域并实际更新我包装到函数中的路径的工作:

function drawChart() {
    yScale.domain([0, d3.max(data, function (d) {
        return d3.max(d.values, function (d) {
            return d.y0 + d.y;
        });
    })]);

    // new data items need to get added
    var areas = svg.selectAll(".layer")
    .data(data, function(d) { return d.type; });

    areas.enter()
        .append("path")
        .attr("class", "layer")
        .style("fill", function (d, i) {
            return colors(i)
    });

    // Added and updated items need to be updated
    areas.attr("d", function (d) {
        return area(d.values);
    })

    // Old items need to be removed - we should not actually need this with the data as it is
    areas.exit().remove();

    svg.selectAll("g.y.axis").call(yAxis);
}

这实际上只是一个开始,您将希望根据自己的需要进行重组,但请查看更新的小提琴

于 2013-06-27T18:34:58.360 回答