d3.js 散点图 - 缩放/拖动边界、缩放按钮、重置缩放、计算中值 [英] d3.js scatter plot - zoom/drag boundaries, zoom buttons, reset zoom, calculate median

查看:68
本文介绍了d3.js 散点图 - 缩放/拖动边界、缩放按钮、重置缩放、计算中值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经构建了一个带有缩放/平移功能的 d3.js 散点图.您可以在此处查看完整内容(单击在新窗口中打开"以查看完整内容):http://bl.ocks.org/129f64bfa2b0d48d27c9

I've built a d3.js scatter plot with zoom/pan functionality. You can see the full thing here (click 'Open in a new window' to see the whole thing): http://bl.ocks.org/129f64bfa2b0d48d27c9

有几个我一直无法弄清楚的功能,如果有人能指出我正确的方向,我会很乐意帮助它:

There are a couple of features that I've been unable to figure out, that I'd love a hand with it if someone can point me in the right direction:

  1. 我想对区域应用 X/Y 缩放/平移边界,以便您无法将其拖到某个点(例如零)以下.
  2. 我还尝试过创建 Google 地图样式的 +/- 缩放按钮,但没有成功.有什么想法吗?

更不重要的是,还有几个方面我已经找到了解决方案,但非常粗糙,所以如果您有更好的解决方案,请告诉我:

Much less importantly, there are also a couple of areas where I've figured out a solution but it's very rough, so if you have a better solution then please do let me know:

  1. 我添加了一个重置缩放"按钮,但它只是删除图形并在其位置生成一个新图形,而不是实际缩放对象.理想情况下,它实际上应该重置缩放.
  2. 我已经编写了自己的函数来计算 X 和 Y 数据的中值.但是我确信必须有更好的方法来使用 d3.median 来做到这一点,但我不知道如何使它工作.

  1. I've added a 'reset zoom' button but it merely deletes the graph and generates a new one in its place, rather than actually zooming the objects. Ideally it should actually reset the zoom.
  2. I've written my own function to calculate the median of the X and Y data. However I'm sure that there must be a better way to do this with d3.median but I can't figure out how to make it work.

var xMed = median(_.map(data,function(d){ return d.TotalEmployed2011;}));
var yMed = median(_.map(data,function(d){ return d.MedianSalary2011;}));

function median(values) {
    values.sort( function(a,b) {return a - b;} );
    var half = Math.floor(values.length/2);

    if(values.length % 2)
        return values[half];
    else
        return (parseFloat(values[half-1]) + parseFloat(values[half])) / 2.0;
};

下面是一个非常简化(即旧)的 JS 版本.您可以在 https://gist.github.com/richardwestenra/找到完整的脚本129f64bfa2b0d48d27c9#file-main-js

A very simplified (i.e. old) version of the JS is below. You can find the full script at https://gist.github.com/richardwestenra/129f64bfa2b0d48d27c9#file-main-js

d3.csv("js/AllOccupations.csv", function(data) {

    var margin = {top: 30, right: 10, bottom: 50, left: 60},
        width = 960 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;

    var xMax = d3.max(data, function(d) { return +d.TotalEmployed2011; }),
        xMin = 0,
        yMax = d3.max(data, function(d) { return +d.MedianSalary2011; }),
        yMin = 0;

    //Define scales
    var x = d3.scale.linear()
        .domain([xMin, xMax])
        .range([0, width]);

    var y = d3.scale.linear()
        .domain([yMin, yMax])
        .range([height, 0]);

    var colourScale = function(val){
        var colours = ['#9d3d38','#c5653a','#f9b743','#9bd6d7'];
        if (val > 30) {
            return colours[0];
        } else if (val > 10) {
            return colours[1];
        } else if (val > 0) {
            return colours[2];
        } else {
            return colours[3];
        }
    };


    //Define X axis
    var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom")
        .tickSize(-height)
        .tickFormat(d3.format("s"));

    //Define Y axis
    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left")
        .ticks(5)
        .tickSize(-width)
        .tickFormat(d3.format("s"));

    var svg = d3.select("#chart").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 + ")")
        .call(d3.behavior.zoom().x(x).y(y).scaleExtent([1, 8]).on("zoom", zoom));

    svg.append("rect")
        .attr("width", width)
        .attr("height", height);

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

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

    // Create points
    svg.selectAll("polygon")
        .data(data)
        .enter()
        .append("polygon")
        .attr("transform", function(d, i) {
            return "translate("+x(d.TotalEmployed2011)+","+y(d.MedianSalary2011)+")";
        })
        .attr('points','4.569,2.637 0,5.276 -4.569,2.637 -4.569,-2.637 0,-5.276 4.569,-2.637')
        .attr("opacity","0.8")
        .attr("fill",function(d) {
            return colourScale(d.ProjectedGrowth2020);
        });

    // Create X Axis label
    svg.append("text")
        .attr("class", "x label")
        .attr("text-anchor", "end")
        .attr("x", width)
        .attr("y", height + margin.bottom - 10)
        .text("Total Employment in 2011");

    // Create Y Axis label
    svg.append("text")
        .attr("class", "y label")
        .attr("text-anchor", "end")
        .attr("y", -margin.left)
        .attr("x", 0)
        .attr("dy", ".75em")
        .attr("transform", "rotate(-90)")
        .text("Median Annual Salary in 2011 ($)");


    function zoom() {
      svg.select(".x.axis").call(xAxis);
      svg.select(".y.axis").call(yAxis);
      svg.selectAll("polygon")
            .attr("transform", function(d) {
                return "translate("+x(d.TotalEmployed2011)+","+y(d.MedianSalary2011)+")";
            });
    };
    }
});

任何帮助将不胜感激.谢谢!

Any help would be massively appreciated. Thanks!

以下是我使用的修复的摘要,基于以下 Superboggly 的建议:

Here is a summary of the fixes I used, based on Superboggly's suggestions below:

    // Zoom in/out buttons:
    d3.select('#zoomIn').on('click',function(){
        d3.event.preventDefault();
        if (zm.scale()< maxScale) {
            zm.translate([trans(0,-10),trans(1,-350)]);
            zm.scale(zm.scale()*2);
            zoom();
        }
    });
    d3.select('#zoomOut').on('click',function(){
        d3.event.preventDefault();
        if (zm.scale()> minScale) {
            zm.scale(zm.scale()*0.5);
            zm.translate([trans(0,10),trans(1,350)]);
            zoom();
        }
    });
    // Reset zoom button:
    d3.select('#zoomReset').on('click',function(){
        d3.event.preventDefault();
        zm.scale(1);
        zm.translate([0,0]);
        zoom();
    });


    function zoom() {

        // To restrict translation to 0 value
        if(y.domain()[0] < 0 && x.domain()[0] < 0) {
            zm.translate([0, height * (1 - zm.scale())]);
        } else if(y.domain()[0] < 0) {
            zm.translate([d3.event.translate[0], height * (1 - zm.scale())]);
        } else if(x.domain()[0] < 0) {
            zm.translate([0, d3.event.translate[1]]);
        }
        ...
    };

我使用的缩放平移非常特别,基本上使用任意常量来或多或少地将定位保持在正确的位置.这并不理想,我愿意接受对更普遍的声音技术的建议.但是,它在这种情况下工作得很好.

The zoom translation that I used is very ad hoc and basically uses abitrary constants to keep the positioning more or less in the right place. It's not ideal, and I'd be willing to entertain suggestions for a more universally sound technique. However, it works well enough in this case.

推荐答案

从中值函数开始,只需要一个数组和一个可选的访问器.所以你可以像使用 max 一样使用它:

To start with the median function just takes an array and an optional accessor. So you can use it the same way you use max:

var med = d3.median(data, function(d) { return +d.TotalEmployed2011; });

至于其他人,如果您取消缩放行为,则可以更好地控制它.所以例如而不是

As for the others if you pull out your zoom behaviour you can control it a bit better. So for example instead of

var svg = d3.select()...call(d3.behavior.zoom()...) 

试试:

var zm = d3.behavior.zoom().x(x).y(y).scaleExtent([1, 8]).on("zoom", zoom);
var svg = d3.select()...call(zm);

然后可以直接设置缩放级别和平移:

Then you can set the zoom level and translation directly:

function zoomIn() {
   zm.scale(zm.scale()*2);
   // probably need to compute a new translation also
}

function reset() {
   zm.scale(1);
   zm.translate([0,0]);
}

限制平移范围有点棘手.当缩放功能中的平移或比例不符合您的喜好时,您可以简单地不更新(或将缩放的平移"设置为您需要的值).像(我认为在你的情况下):

Restricting the panning range is a bit trickier. You can simply not update when the translate or scale is not to your liking inside you zoom function (or set the zoom's "translate" to what you need it to be). Something like (I think in your case):

function zoom() {
    if(y.domain()[0] < 0) {
        // To restrict translation to 0 value
        zm.translate([d3.event.translate[0], height * (1 - zm.scale())]);
    }
    ....
}        

请记住,如果您想放大以允许轴上出现负数,但不向您平移,则会发现您会遇到一些棘手的情况.

Keep in mind that if you want zooming in to allow a negative on the axis, but panning not to you will find you get into some tricky scenarios.

这可能会过时,但请查看 限制域何时在 D3.js 中缩放或平移

This might be dated, but check out Limiting domain when zooming or panning in D3.js

另请注意,缩放行为确实具有限制平移和缩放的功能一个点.但该代码在后来的更新中被取出.

Note also that the zoom behaviour did have functionality for limiting panning and zooming at one point. But the code was taken out in a later update.

这篇关于d3.js 散点图 - 缩放/拖动边界、缩放按钮、重置缩放、计算中值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆