D3焦点/上下文图表:刷选和平移的同步

3

我有一个d3焦点/上下文图表,在笔刷上下文之后,我希望能够在焦点部分进行平移,并且我希望上下文区域的笔刷部分与焦点区域的平移同步。然而,当我在选择上下文图表中的区域后单击焦点部分时,焦点比例尺会改变,点不再显示在正确的坐标上。以下代码也可在jsfiddle上找到:

<html>
   <head>
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <title> - jsFiddle demo</title>
      <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
      <link rel="stylesheet" type="text/css" href="/css/result-light.css">
      <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
      <style type="text/css">
         circle {
         -webkit-transition: fill-opacity 250ms linear;
         }
         .selecting circle {
         fill-opacity: .5;
         }
         .selecting circle.selected {
         stroke: #f00;
         }
         .brush .extent {
         stroke: #B8C6D0;
         fill-opacity: .125;
         shape-rendering: crispEdges;
         }
         #context .axis path.domain {
         stroke: lightsteelblue;
         stroke-width: 5px;
         }
         #context .tick {
         stroke:black;
         stroke-width: 1px;
         }
         #context .x .tick {
         stroke:black;
         stroke-width: 2px;
         }
         .axis path, .axis line {
         fill: none;
         stroke: #ddd;
         stroke-width: 1px;
         shape-rendering: crispEdges;
         }
         .axis path {
         stroke: #999;
         stroke-width: 2px;
         }
      </style>
      <script type="text/javascript">//<![CDATA[ 
    var data = [{
        Id: "1",
        Year: 1950,
        Relevance: 55,
        Category: "Cat1",
        SpecFreq: 5,
        GenFreq: 10
    }, {
        Id: "2",
        Year: 1975,
        Relevance: 25,
        Category: "Cat1",
        SpecFreq: 2,
        GenFreq: 31
    }, {
        Id: "3",
        Year: 1990,
        Relevance: 75,
        Category: "Cat1",
        SpecFreq: 8,
        GenFreq: 23
    }, {
        Id: "4",
        Year: 1970,
        Relevance: 45,
        Category: "Cat1",
        SpecFreq: 17,
        GenFreq: 60
    }, {
        Id: "5",
        Year: 1985,
        Relevance: 90,
        Category: "Cat1",
        SpecFreq: 17,
        GenFreq: 25
    }];

    $(function () {
        //dimensions
        var margin = {
            top: 5.5,
            right: 19.5,
            bottom: 39.5,
            left: 39.5
        };

        //data domain extents
        var extentX = d3.extent(data, function (d) {
            return d.Year;
        });
        var extentY = d3.extent(data, function (d) {
            return d.Relevance;
        });

        var focusAxisOptions = {
            x: {
                ticks: {
                    format: d3.format("d"),
                    size: -1* (500 - margin.top - margin.bottom),
                    ticks: 10
                },
                showLabel: true
            },
            y: {
                ticks: {
                    format: d3.format(""),
                    size: -1 * (800 - margin.left - margin.right),
                    ticks: 10
                },
                showLabel: true
            }
        };

        var contextAxisOptions = {
            x: {
                ticks: {
                    format: d3.format("d"),
                    size: -1 * (100 - margin.top - margin.bottom),
                    ticks: 10
                },
                showLabel: true
            },
            y: {
                ticks: {
                    format: "",
                    size: 0,
                    ticks: 0
                },
                showLabel: false
            }
        };

        var focus = DrawChart(data, margin, 800 - margin.left - margin.right, 500 - margin.top - margin.bottom, extentX, extentY, focusAxisOptions);
        var context = DrawChart(data, margin, 800 - margin.left - margin.right, 100 - margin.top - margin.bottom, extentX, extentY, contextAxisOptions);

        MakeContextBrushable(context, focus);

        MakeFocusZoomable(focus);
    });

    function DrawChart(data, margin, width, height, extentX, extentY, axisOptions) {

        //pad extents to provide some extra "blank" areas around edge of graph
        var paddedExtentX = [extentX[0] - 5, extentX[1] +5];
        var paddedExtentY = [extentY[0] - 5, extentY[1] +5];

        //scales
        var x = d3.scale.linear().domain(paddedExtentX).range([0, width]);
        var y = d3.scale.linear().domain(paddedExtentY).range([height, 0]);
        var radiusMax = .025 * width;
        var radius = d3.scale.sqrt().domain([0, 100]).range([0, radiusMax]);
        var color = d3.scale.ordinal().domain(["Cat1", "Cat2", "Cat3"]).range(["#b7b8a0", "#898a72", "#878772"]);

        //axes
        var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(axisOptions.x.ticks.format).tickSize(axisOptions.x.ticks.size).ticks(axisOptions.x.ticks.ticks);
        var yAxis = d3.svg.axis().scale(y).orient("left").tickFormat(axisOptions.y.ticks.format).tickSize(axisOptions.y.ticks.size).ticks(axisOptions.y.ticks.ticks);

        //create and size svg element
        var svg = d3.select("#chart").append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .style("float", "left")
            .style("clear", "left");

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

        // Add the x-axis.
        g.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + height + ")")
            .call(xAxis)
            .selectAll("text")
            .style("font-size", "10px")
            .attr("dy", "1.5em");

        // Add the y-axis.
        g.append("g")
            .attr("class", "y axis")
            .call(yAxis)
            .selectAll("text")
            .style("font-size", "10px")
            .attr("dx", "-1em");

        // Add the x-axis label.
        if (axisOptions.x.showLabel) {
            g.append("text")
            .attr("class", "x label")
            .attr("text-anchor", "end")
            .attr("x", width / 2)
            .attr("y", height + 35)
            .text(" Year");
        }

        // Add the y-axis label.
        if (axisOptions.y.showLabel) {
            g.append("text")
            .attr("class", "y label")
            .attr("text-anchor", "end")
            .attr("x", -1 * height / 2)
            .attr("y", -40)
            .attr("dy", ".75em")
            .attr("transform", "rotate(-90)")
            .text("Relevance");
        }

        //plot genFreq
        var gGenerally = g.append("g").selectAll("circle")
            .data(data)
            .enter().append("circle")
            .style("fill", function (d) {
                return color(d.Category);
            })
            .attr("cx", function (d) {
                return x(d.Year);
            })
            .attr("cy", function (d) {
                return y(d.Relevance);
            })
            .attr("r", function (d) {
                return radius(d.GenFreq);
            })
            .append("title")
            .text(function (d) {
                return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
            });

        //plot specFreq
        var gWithin = g.append("g").selectAll("circle")
            .data(data)
            .enter().append("circle")
            .style("fill", function (d) {
                return "#d6d487";
            })
            .attr("cx", function (d) {
                return x(d.Year);
            })
            .attr("cy", function (d) {
                return y(d.Relevance);
            })
            .attr("r", function (d) {
                return radius(d.SpecFreq);
            })
            .append("title")
            .text(function (d) {
                return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
            });

        var chart = {
            svg: svg,
            g: g,
            x: x,
            y: y,
            xAxis: xAxis,
            yAxis: yAxis
        }
        return chart;
    }

    function MakeContextBrushable(context, focus) {
        var brush = d3.svg.brush().x(context.x).y(context.y)
        .on("brushstart", brushstart)
        .on("brush", brushmove)
        .on("brushend", brushend);

        context.g.append("g")
        .attr("class", "brush")
        .call(brush);

        function brushstart() {
            context.svg.classed("selecting", true);
        }

        function brushmove() {
            var e = d3.event.target.extent();
            var circle = context.svg.selectAll("circle");
            circle.classed("selected", function (d) {
                return e[0][0] <= d["DecisionYear"] && d["DecisionYear"] <= e[1][0]
            && e[0][1] <= d["Relevance"] && d["Relevance"] <= e[1][1];
            });
        }

        function brushend() {
            context.svg.classed("selecting", !d3.event.target.empty());
            if (!d3.event.target.empty()) {
                var e = d3.event.target.extent();
                focus.x.domain([e[0][0], e[1][0]]);
                focus.y.domain([e[0][1], e[1][1]]);

                focus.g.select(".x.axis").call(focus.xAxis)
                    .selectAll("text")
                    .style("font-size", "10px")
                    .attr("dx", "-1em");
                focus.g.select(".y.axis").call(focus.yAxis)
                    .selectAll("text")
                    .style("font-size", "10px")
                    .attr("dx", "-1em");
                var circle = focus.svg.selectAll("circle").attr("cx", function (d) { return focus.x(d.Year); })
                .attr("cy", function (d) { return focus.y(d.Relevance); })
                console.log("BrushEnd Domain: [" + focus.x.domain()[0] + ", " + focus.x.domain()[1] + "]");
            }
            else {
                focus.x.domain(context.x.domain());
                focus.y.domain(context.y.domain());
                focus.g.select(".x.axis").call(focus.xAxis)
                    .selectAll("text")
                    .style("font-size", "10px")
                    .attr("dx", "-1em");
                focus.g.select(".y.axis").call(focus.yAxis)
                    .selectAll("text")
                    .style("font-size", "10px")
                    .attr("dx", "-1em");
                var circle = focus.g.selectAll("circle").attr("cx", function (d) { return focus.x(d.Year); })
                .attr("cy", function (d) { return focus.y(d.Relevance); })
            }
        }
    }

    function MakeFocusZoomable(focus) {
        focus.svg.call(d3.behavior.zoom().x(focus.x).y(focus.y).on("zoom", Zoom));

        function Zoom() {
            focus.svg.select(".x.axis").call(focus.xAxis).selectAll("text")
                        .style("font-size", "10px")
                        .attr("dy", "1.5em");

            focus.svg.select(".y.axis").call(focus.yAxis).selectAll("text")
                        .style("font-size", "10px")
                        .attr("dx", "-1em");

            focus.svg.selectAll("circle").attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
        }
    }
      </script>
   </head>
   <body>
      <div id="chart">
      </div>
   </body>
</html>
1个回答

3
重置刷子以便在平移后重新开始,您需要重设并再次调用brush.extent:
//Find extent of zoomed area, for example the edges of graphed region
var brushExtent = [x.invert(0), x.invert(width)];
context.select(".brush").call(brush.extent(brushExtent));

这是一份关于针对线型图的焦点/上下文刷选和平移同步性的示例: http://jsfiddle.net/MtXvx/8/ 希望对您有所帮助!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接