2013-02-25 174 views
6

我已經用縮放/平移功能構建了d3.js散點圖。你可以在這裏看到完整的東西(點擊'在新窗口中打開'查看整個東西): http://bl.ocks.org/129f64bfa2b0d48d27c9d3.js散點圖 - 縮放/拖動邊界,縮放按鈕,重置縮放,計算中位數

有一些功能我一直無法弄清楚,如果有人可以指引我在正確的方向:

  1. 我想將X/Y縮放/平移邊界應用到該區域,以便您不能將其拖動到某個點以下(例如零)。
  2. 我也嘗試過創建Google地圖樣式+/-縮放按鈕,沒有任何成功。有任何想法嗎?

少得多重要的是,也有一對夫婦在那裏我已經想出了一個解決方案領域,但它是非常粗糙的,所以如果你有一個更好的解決方案,那麼請不要讓我知道:

  1. 我添加了一個「重置縮放」按鈕,但它只是刪除圖形並在其位置生成一個新圖形,而不是實際縮放對象。理想情況下,它實際上應該重置縮放。
  2. 我寫了自己的函數來計算X和Y數據的中位數。不過,我確信用d3.median一定有更好的方法來做到這一點,但我無法弄清楚如何使它工作。

    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

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)+")"; 
      }); 
    }; 
    } 
}); 

找到完整的腳本任何幫助將大規模讚賞。謝謝!

編輯:這是我所使用的修補程序的總結,基於以下Superboggly的建議:

// 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]]); 
     } 
     ... 
    }; 

,我使用的變焦翻譯非常臨時性的,基本上都採用abitrary常量保持定位更或少在正確的地方。這並不理想,我願意爲更普遍的技術提供建議。然而,在這種情況下,它運行得很好。

回答

11

要開始使用中值函數只需要一個數組和一個可選的訪問器。所以,你可以使用它,你使用最多的相同方式:

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

至於其他人,如果你拿出你的縮放行爲,你可以控制它好一點。因此,例如,而不是

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); 

然後你就可以直接設置縮放級別和翻譯:

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

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

限制平移範圍是有點棘手。當翻譯或縮放不符合你的需要時,你可以簡單地不更新縮放功能(或將縮放的「翻譯」設置爲你所需要的)。喜歡的東西(我想你的情況):

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

請記住,如果你想放大到允許在軸負,但不是平移來,你會發現你進入一些棘手的場景。

這可能是過時的,但檢查出Limiting domain when zooming or panning in D3.js

還要注意縮放行爲也有限制平移和在one point縮放功能。但代碼在以後的update中被取出。

+0

再次感謝Superboggly!您的zoomIn/reset代碼似乎可行,但縮放比例僅在下一個縮放事件(即拖動或鼠標滾輪)時發生變化。我很難在點擊按鈕時更新內容。它一定很簡單,但我無法弄清楚。我的按鈕點擊代碼是: d3.select('#zoomIn')。call(zoom).on('click',function(){ d3.event.preventDefault(); zm.scale(zm.scale )* 2); }); 我試過各種使用.call(),zoom()和.on()無濟於事。 – richardwestenra 2013-02-26 15:46:38

+0

我在我的[最後jsfiddle]底部添加了一個快速的藍色縮放廣場(http://jsfiddle.net/superboggly/SD5cK/2/)。您所缺少的是在設置比例後調用zoom()。考慮您應用由行爲計算的縮放狀態時提供的縮放功能。 – Superboggly 2013-02-26 23:46:39

+0

好吧!排序!事實證明,這正是我正在做的,但它不適合我......長話短說:當我使用版本2時,您正在使用d3版本3,這就是爲什麼當我放棄您的時候它不起作用在我的代碼中的藍色方塊。已升級到v3,現在可以運行。歡呼:) – richardwestenra 2013-02-28 11:40:46

-1

我不喜歡重新發明輪子。我正在尋找允許縮放的散點圖。 Highcharts就是其中之一,但是有一種情節,它基於D3,不僅可以縮放,還可以在散點圖上也有線數據集,我希望使用我的一些數據集,而且很難找到其他數據集圖庫。我給它一個嘗試:

https://plot.ly/javascript/line-and-scatter/

https://github.com/plotly/plotly.js

使用這樣好的庫可以爲您節省大量的時間和疼痛。