d3.js - D3 焦点/上下文图表 : synchronization of brushing and panning

标签 d3.js pan brush

我有一个 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>

最佳答案

要在平移后重置刷亮,您需要重置并再次调用 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/

希望对您有所帮助!

关于d3.js - D3 焦点/上下文图表 : synchronization of brushing and panning,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15818217/

相关文章:

c# - emgucv:C#中的Pan Card不当偏斜检测

javascript - d3.js 带有刷色的散点矩阵 - 比例误差

javascript - 跨不同对象同步画笔

javascript - 键未正确绑定(bind)到 D3 中的元素

javascript - 保证D3中的刻度数

javascript - 使用 D3 在鼠标悬停时更改元素大小

javascript - 如何通过 ID 选择元素 (d3)

image - 如何在 Delphi 中从合成图像创建平移

javascript - HTML5 canvas paint 调色工具