为什么在交互式多重系列折线图中使用brushed()时只有一条线被扩展

时间:2015-01-21 04:59:16

标签: javascript svg d3.js

我正在使用d3.js处理多系列折线图,我正在尝试实现焦点和上下文缩放,如http://bl.ocks.org/mbostock/1667367所示。我已将示例中的面积图转换为折线图有一个单一的系列,但我无法弄清楚为什么我的所有行都没有进行扩展,同时使用brush..only x-domain扩展x域,并且根据新的x域范围扩展了一行....

这是我正在使用的代码......

var margin = {
        top: 20,
        right: 200,
        bottom: 100,
        left: 100
    },
    margin2 = {
        top: 430,
        right: 10,
        bottom: 20,
        left: 100
    },
    height = 500 - margin.top - margin.bottom,
    height2 = 500 - margin2.top - margin2.bottom,
    legendPanel = {
        width: 80
    },
    width = 960 - margin.left - margin.right - legendPanel.width;

var data = [{
    "symbol": "Banker",
    "date": "20000112",
    "earnedpts": "30",
    "redeemedpts": "15"
}, {
    "symbol": "Banker",
    "date": "20000810",
    "earnedpts": "10",
    "redeemedpts": "8"
}, {
    "symbol": "Banker",
    "date": "20010618",
    "earnedpts": "50",
    "redeemedpts": "20"
}, {
    "symbol": "Banker",
    "date": "20011220",
    "earnedpts": "40",
    "redeemedpts": "20"
}, {
    "symbol": "Banker",
    "date": "20020220",
    "earnedpts": "30",
    "redeemedpts": "20"
}, {
    "symbol": "Banker",
    "date": "20021130",
    "earnedpts": "10",
    "redeemedpts": "5"
}, {
    "symbol": "Health",
    "date": "20000112",
    "earnedpts": "20",
    "redeemedpts": "15"
}, {
    "symbol": "Health",
    "date": "20000810",
    "earnedpts": "40",
    "redeemedpts": "28"
}, {
    "symbol": "Health",
    "date": "20010618",
    "earnedpts": "30",
    "redeemedpts": "20"
}, {
    "symbol": "Health",
    "date": "20011220",
    "earnedpts": "60",
    "redeemedpts": "35"
}, {
    "symbol": "Health",
    "date": "20020220",
    "earnedpts": "30",
    "redeemedpts": "25"
}, {
    "symbol": "Health",
    "date": "20021130",
    "earnedpts": "20",
    "redeemedpts": "15"
}, {
    "symbol": "Govt",
    "date": "20000112",
    "earnedpts": "80",
    "redeemedpts": "65"
}, {
    "symbol": "Govt",
    "date": "20000810",
    "earnedpts": "60",
    "redeemedpts": "48"
}, {
    "symbol": "Govt",
    "date": "20010618",
    "earnedpts": "60",
    "redeemedpts": "40"
}, {
    "symbol": "Govt",
    "date": "20011220",
    "earnedpts": "30",
    "redeemedpts": "15"
}, {
    "symbol": "Govt",
    "date": "20020220",
    "earnedpts": "40",
    "redeemedpts": "25"
}, {
    "symbol": "Govt",
    "date": "20021130",
    "earnedpts": "30",
    "redeemedpts": "5"
}, {
    "symbol": "BPO",
    "date": "20000112",
    "earnedpts": "70",
    "redeemedpts": "45"
}, {
    "symbol": "BPO",
    "date": "20000810",
    "earnedpts": "60",
    "redeemedpts": "30"
}, {
    "symbol": "BPO",
    "date": "20010618",
    "earnedpts": "40",
    "redeemedpts": "25"
}, {
    "symbol": "BPO",
    "date": "20011220",
    "earnedpts": "70",
    "redeemedpts": "50"
}, {
    "symbol": "BPO",
    "date": "20020220",
    "earnedpts": "40",
    "redeemedpts": "25"
}, {
    "symbol": "BPO",
    "date": "20021130",
    "earnedpts": "50",
    "redeemedpts": "30"
}];

var parsedate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale().range([0, width]);
x2 = d3.time.scale()
    .range([0, width]);
var y = d3.scale.linear().range([height, 0]);
y2 = d3.scale.linear().range([height2, 0]);

var xAxis = d3.svg.axis().scale(x)
    .orient("bottom");

var xAxis2 = d3.svg.axis()
    .scale(x2)
    .orient("bottom");

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

var color = d3.scale.ordinal()
    .range(["#04b0ff", "#04ffa8", "#ff5404", "#8a89a6", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);

var brush = d3.svg.brush()
    .x(x2)
    .on("brush", brushed);

var extent = brush.extent();
var rangeExtent = [x2(extent[0]), x2(extent[1])];
var rangeWidth = rangeExtent[1] - rangeExtent[0];

var priceline1 = d3.svg.line()
    .x(function(d) {
        return x(d.date);
    })
    .y(function(d) {
        return y(d.earnedpts);
    })
    .interpolate("linear");

var priceline2 = d3.svg.line()
    .x(function(d) {
        return x(d.date);
    })
    .y(function(d) {
        return y(d.redeemedpts);
    })
    .interpolate("linear");

var svg = d3.select("body")
    .append("svg")
    .attr("width", width + margin.left + margin.right + legendPanel.width)
    .attr("height", height + margin.top + margin.bottom);

var focus = svg.append("g")
    .attr("class", "focus")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var context = svg.append("g")
    .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")")
    .attr("class", "context");

svg.append("defs")
    .append("clipPath")
    .attr("id", "clip")
    .append("rect")
    .attr("width", width)
    .attr("height", height);

data.forEach(function(d) {
    d.date = parsedate(d.date);
    d.earnedpts = +d.earnedpts;
    d.redeemedpts = +d.redeemedpts;
});

x.domain(d3.extent(data, function(d) {
    return d.date;
}));
x2.domain(x.domain());
y.domain([0, d3.max(data, function(d) {
    return Math.max(d.earnedpts, d.redeemedpts);
})]);
y2.domain(y.domain());

var dataNest = d3.nest()
    .key(function(d) {
        return d.symbol;
    })
    .entries(data);

dataNest.forEach(function(d) {
    focus.append("path")
        .attr("class", "line")
        .style("stroke", function() {
            return d.color = color(d.key);
        })
        .attr("d", priceline1(d.values))
});

dataNest.forEach(function(d) {
    focus.append("path")
        .attr("class", "line")
        .style("stroke", function() {
            return d.color = color(d.key);
        })
        .style("stroke-dasharray", ("3, 3"))
        .attr("d", priceline2(d.values))
});

context.append("g")
    .attr("class", "x axis1")
    .attr("transform", "translate(0," + height2 + ")")
    .call(xAxis2);

var contextArea = d3.svg.area()
    .interpolate("monotone")
    .x(function(d) {
        return x2(d.date);
    })
    .y0(height2)
    .y1(0);

context.append("path")
    .attr("class", "area")
    .attr("d", contextArea(dataNest[0].values))
    .attr("fill", "#F1F1F2");

context.append("g")
    .attr("class", "x brush")
    .call(brush)
    .selectAll("rect")
    .attr("height", height2)
    .attr("fill", "#E6E7E8");

focus.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
    .append("text")
    .attr("x", -30)
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Days");

focus.append("g")
    .attr("class", "y axis")
    .call(yAxis)
    .append("text")
    .attr("transform", "rotate(-90)")
    .attr("x", 0)
    .attr("y", -40)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Average points Earned & Redeemed");


dataNest.forEach(function(d, i) {
    focus.append("text")
        .attr("transform", "translate(" + width + "," + y(data[dataNest[i].values.length + i * 6 - 1].earnedpts) + ")")
        .attr("dy", ".35em")
        .attr("text-anchor", "start")
        .style("fill", function() {
            return d.color = color(d.key);
        })
        .text(d.key);
});

focus.append("text")
    .attr("transform", "translate(" + (width - 20) + ",5)")
    .attr("dy", ".35em")
    .attr("text-anchor", "start")
    .style("fill", "black")
    .text("Points Earned");

focus.append("text")
    .attr("transform", "translate(" + (width - 20) + "," + (y(data[0].redeemedpts) - 245) + ")")
    .attr("dy", ".35em")
    .attr("text-anchor", "start")
    .style("fill", "black")
    .text("Points Redeemed");

function brushed() {
    x.domain(brush.empty() ? x2.domain() : brush.extent());
    focus.select(".x.axis").transition().call(xAxis);
    focus.select(".y.axis").transition().call(yAxis);
    dataNest.forEach(function(d) {
        focus.selectAll("path")
            .style("stroke", function() {
                return d.color = color(d.key);
            })
            .transition()
            .attr("d", priceline1(d.values))
    });
}

1 个答案:

答案 0 :(得分:0)

我认为这里有一些问题。

首先,使用两种不同的线方法创建两种类型的线(虚线和实线)。那样就好。但是,在最后的更新方法中,您选择所有这些方法并仅使用一种绘图方法(priceline)。我建议进行以下更改:

  1. 为每一行添加一个类标识符,例如line1line2,如下所示:

    dataNest.forEach(function (d) {
     focus.append("path")
       .attr("class", "line line1-" + d.key)
       .style("stroke", function () {
       return color(d.key);
     })
    .attr("d", priceline1(d.values));
    });
    

    dataNest.forEach(function (d) {
     focus.append("path")
       .attr("class", "line line2-" + d.key)
       .style("stroke", function () {
      return color(d.key);
    })
    .style("stroke-dasharray", ("3, 3"))
    .attr("d", priceline2(d.values));
    });
    
  2. 然后,在您的画笔事件中,选择适当的一个并更新该行。 (道歉,我不知道你要更新哪一个,所以我把它们都包括在内)

    function brushed() {
        x.domain(brush.empty() ? x2.domain() : brush.extent());
        focus.select(".x.axis").transition().call(xAxis);
        focus.select(".y.axis").transition().call(yAxis);
        dataNest.forEach(function (d) {
            focus.selectAll(".line1-" + d.key)
                .transition().duration(200)
                .attr("d", priceline1(d.values));
            focus.selectAll(".line2-" + d.key)
                .transition().duration(200)
                .attr("d", priceline2(d.values));
        });
    
    }
    
  3. 小提琴是here

    关于您的比例格式,您可以使用tickFormat方法,如下所示:

    // First define a formatting method such as
    var axisFormat = d3.time.format("%b")
    
    // Then apply your tickFormat to your scale
    var xAxis = d3.svg.axis()
                      .scale(x)
                      .orient("bottom")
                      .tickFormat(function(d) {
                          v=axisFormat(d); 
                          return v.substr(0,1) // you can adjust this.
                       });
    

    希望这有帮助