如何链接D3缩放和平移控件

时间:2016-02-01 16:17:55

标签: d3.js

我想将多个图表的缩放和平移控制链接在一起,以便在其中一个图表平移和缩放控件启用时它们全部平移和缩放。

我尝试创建一个缩放对象并将其传递给图表,但只有最后一个图表实际上是平移和缩放的。其他人保持静止,即使我在他们的区域放大。

这是图表的快照。每个图表都有一个概览图表,其中包含一个可以移动的视口。我想将所有控件链接在一起,因此每个图表上的视口也是相同的。

那么,如何在多个图表上链接平移和缩放控件?

以下是此代码的jsfiddle:https://jsfiddle.net/babazaroni/a52oukzn/

enter image description here

这是我的代码:

整体图表创建者。此代码生成3个图表。

define([
    'd3',
    'components/sl',
    'MockData',
    'components/candlestickSeries',
    'Chart'
], function (d3, sl, MockData,candlestickSeries,Chart) {
    'use strict';




    function generateData()
    {

      var data = new MockData(0.1, 0.1, 100, 50, function (moment) {
        return !(moment.day() === 0 || moment.day() === 6);
      })
      .generateOHLC(new Date(2014, 1, 1), new Date(2014, 8, 1));

      return data;
    }


    var data = generateData();


  	d3.select('#chart1')
      	.datum(data)
    	.call(Chart());

     data = generateData();



    d3.select('#chart1')
        .datum(data)
      .call(Chart());

         data = generateData();


          d3.select('#chart1')
        .datum(data)
      .call(Chart());




});

这是图表代码:

define([
    'd3',
    'components/sl',
    'MockData',
    'components/candlestickSeries'
], function (d3, sl, MockData) {
    'use strict';

    function timeSeriesChart() {

        function chart(selection)
        {
            selection.each(function(data) {


                var minDate = new Date(d3.min(data, function (d) { return d.date; }).getTime() - 8.64e7);
                var maxDate = new Date(d3.max(data, function (d) { return d.date; }).getTime() + 8.64e7);
                var yMin = d3.min(data, function (d) { return d.low; });
                var yMax = d3.max(data, function (d) { return d.high; });



                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // The primary chart

                // Set up the drawing area
    
                var margin = {top: 20, right: 20, bottom: 30, left: 35},
                    width = 600 - margin.left - margin.right,
                    height = 200 - margin.top - margin.bottom;

                var plotChart = d3.select(this).classed('chart', true).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 + ')');

                var plotArea = plotChart.append('g')
                    .attr('clip-path', 'url(#plotAreaClip)');

                plotArea.append('clipPath')
                    .attr('id', 'plotAreaClip')
                    .append('rect')
                    .attr({ width: width, height: height });

                // Scales

                var xScale = d3.time.scale(),
                    yScale = d3.scale.linear();



                // Set scale domains
                xScale.domain([minDate, maxDate]);
                yScale.domain([yMin, yMax]).nice();

                // Set scale ranges
                xScale.range([0, width]);
                yScale.range([height, 0]);

                // Axes

                var xAxis = d3.svg.axis()
                    .scale(xScale)
                    .orient('bottom')
                    .ticks(10);

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

                plotChart.append('g')
                    .attr('class', 'x axis')
                    .attr('transform', 'translate(0,' + height + ')')
                    .call(xAxis);

                plotChart.append('g')
                    .attr('class', 'y axis')
                    .call(yAxis);

                plotChart.append("text")
                    .attr("x", (width / 2))             
                    .attr("y", 1 - (margin.top / 2))
                    .attr("text-anchor", "middle")  
                    .style("font-size", "16px") 
                    .style("text-decoration", "underline")  
                    .text("Your title goes here");

                // Data series

                var series = sl.series.candlestick()
                    .xScale(xScale)
                    .yScale(yScale);

                var dataSeries = plotArea.append('g')
                    .attr('class', 'series')
                    .datum(data)
                    .call(series);



                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Navigation chart

                var navWidth = width,
                    navHeight = 100 - margin.top - margin.bottom;

                // Set up the drawing area

                var navChart = d3.select(this).classed('chart', true).append('svg')
                    .classed('navigator', true)
                    .attr('width', navWidth + margin.left + margin.right)
                    .attr('height', navHeight + margin.top + margin.bottom)
                    .append('g')
                    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                // Scales

                var navXScale = d3.time.scale()
                        .domain([
                            new Date(minDate.getTime() - 8.64e7),
                            new Date(maxDate.getTime() + 8.64e7)
                        ])
                        .range([0, navWidth]),
                    navYScale = d3.scale.linear()
                        .domain([yMin, yMax])
                        .range([navHeight, 0]);

                // Axes

                var navXAxis = d3.svg.axis()
                    .scale(navXScale)
                    .orient('bottom');

                navChart.append('g')
                    .attr('class', 'x axis')
                    .attr('transform', 'translate(0,' + navHeight + ')')
                    .call(navXAxis);

                // Data series

                var navData = d3.svg.area()
                    .x(function (d) { return navXScale(d.date); })
                    .y0(navHeight)
                    .y1(function (d) { return navYScale(d.close); });

                var navLine = d3.svg.line()
                    .x(function (d) { return navXScale(d.date); })
                    .y(function (d) { return navYScale(d.close); });

                navChart.append('path')
                    .attr('class', 'data')
                    .attr('d', navData(data));

                navChart.append('path')
                    .attr('class', 'line')
                    .attr('d', navLine(data));



                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Viewport

                function redrawChart() {

                    dataSeries.call(series);
                    plotChart.select('.x.axis').call(xAxis);
                }

                function updateZoomFromChart() {

                    var fullDomain = maxDate - minDate,
                        currentDomain = xScale.domain()[1] - xScale.domain()[0];

                    var minScale = currentDomain / fullDomain,
                        maxScale = minScale * 20;

                    zoom.x(xScale)
                        .scaleExtent([minScale, maxScale]);
                }


                var viewport = d3.svg.brush()
                    .x(navXScale)
                    .on("brush", function () {
                        xScale.domain(viewport.empty() ? navXScale.domain() : viewport.extent());
                        redrawChart();
                    })
                    .on("brushend", function () {
                        updateZoomFromChart();
                    });

                navChart.append("g")
                    .attr("class", "viewport")
                    .call(viewport)
                    .selectAll("rect")
                    .attr("height", navHeight);


                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Zooming and panning

                function updateViewpointFromChart() {

                    if ((xScale.domain()[0] <= minDate) && (xScale.domain()[1] >= maxDate)) {

                        viewport.clear();
                    }
                    else {

                        viewport.extent(xScale.domain());
                    }

                    navChart.select('.viewport').call(viewport);
                }



                var zoom = d3.behavior.zoom()
                    .x(xScale)
                    .on('zoom', function() {
                        if (xScale.domain()[0] < minDate) {
                            zoom.translate([zoom.translate()[0] - xScale(minDate) + xScale.range()[0], 0]);
                        } else if (xScale.domain()[1] > maxDate) {
                            zoom.translate([zoom.translate()[0] - xScale(maxDate) + xScale.range()[1], 0]);
                        }
                        redrawChart();
                        updateViewpointFromChart();
                    });

                var overlay = d3.svg.area()
                    .x(function (d) { return xScale(d.date); })
                    .y0(0)
                    .y1(height);

                plotArea.append('path')
                    .attr('class', 'overlay')
                    .attr('d', overlay(data))
                .call(zoom);

                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Setup

                var daysShown = 30;

                xScale.domain([
                    data[data.length - daysShown - 1].date,
                    data[data.length - 1].date
                ]);

                redrawChart();

                updateViewpointFromChart();

                updateZoomFromChart();


                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Helper methods

            });

        }
 //      alert("here we are again and again");
        return chart;
    }
    return timeSeriesChart;
});

1 个答案:

答案 0 :(得分:1)

从此示例中:http://bl.ocks.org/mbostock/6123708

创建缩放:

var zoom = d3.behavior.zoom()
    .scaleExtent([1, 10])
    .on("zoom", zoomed);

function zoomed() {
  container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}

调用容器缩放:

var svg = d3.select("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.right + ")")
    .call(zoom); //<<<<<HERE

这就是它在单个画布上的样子,但在你的情况下,它看起来像这样:

创建缩放:

var zoom = d3.behavior.zoom()
    .scaleExtent([1, 10])
    .on("zoom", zoomed);

转换每个画布的容器

    function zoomed() {
      container1.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
container2.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
container3.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
    }

在所有容器上调用缩放:

    var svg1 = d3.select("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.right + ")")
        .call(zoom); //<<<<<HERE

var svg2 = d3.select("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.right + ")")
        .call(zoom); //<<<<<HERE

var svg3 = d3.select("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.right + ")")
        .call(zoom); //<<<<<HERE

这些方面的某些内容应该有效,但无需任何示例即可对其进行测试。