如何使用 d3 创建响应式地图

Posted

技术标签:

【中文标题】如何使用 d3 创建响应式地图【英文标题】:How to create a responsive map using d3 【发布时间】:2014-01-20 00:45:44 【问题描述】:

我正在尝试使我的地图具有响应性,因此它会根据浏览器窗口的大小进行调整。这是我目前所拥有的,

style.css

#mapbox 
border:2px solid #000;
width:960px;
height:550px;
background:#FFFFFF;

即.js

var margin = top: 10, left: 10, bottom: 10, right: 10,
width = parseInt(d3.select('#mapbox').style('width')), 
width = width - margin.left - margin.right, 
mapRatio = .5, 
height = width * mapRatio;
.
.
d3.select(window).on('resize', resize);
.
.
function resize()  
    width = parseInt(d3.select('#mapbox').style('width'));
    width = width - margin.left - margin.right;
    height = width * mapRatio;


        projection.translate([width / 2, height / 2])
                .scale(width);

        svg.style('width', width + 'px')
            .style('height', height + 'px');


当我调整窗口大小时,它仍然为我的 svg 容器保持相同的宽度和高度。我猜那是因为,在调整大小函数中,我从 CSS 中获取宽度和高度,无论我调整窗口大小是否正确?

我被困在这里,我错过了什么明显的东西吗?如果您有任何建议让我完成这项工作,那就太好了。

我非常关注以下example,

【问题讨论】:

你见过this question吗? 【参考方案1】:

感谢指点,

这对我有用,但不确定这是否是最佳实践..

html

     <section id = "mapContainer">
        <svg id = "map"  
            viewBox="0 0 960 550"   preserveAspectRatio="xMidYMid"> 
        </svg>
     </section>

javascript

    var svgContainer = $("#map");
    var width = svgContainer.width();
    var height = svgContainer.height();
    var aspect = width/height;
    var container = svgContainer.parent();

    var svg = d3.select("svg");

    //responsive map
function resize()
    // adjust things when the window size changes
    var targetWidth = container.width();

    svg.attr("width", targetWidth);
    svg.attr("height", Math.round(targetWidth/aspect));

    console.log ("width : " + targetWidth);
    console.log ("height : " + Math.round(targetWidth/aspect));

    // update projection
    projection.translate([width / 2, height / 2])
                .scale(width);

    // resize the map
        svg.select('.lga').attr('d', path);

【讨论】:

这个解决方案非常适合我。我是 D3 的新手,我想知道您是否发现了一种新的最佳实践,它涉及的自定义代码更少。【参考方案2】:

您的问题中没有足够的细节来准确说明您在做什么。有一些常见的事情可能会破坏这样的调整大小代码......

您的地图框可能永远不会改变大小。您可以为每次调用调整大小粘贴一个带有宽度/高度的 console.log(...),这会告诉您那里发生了什么。

此外,根据您最初指定 SVG 大小的方式(例如,使用宽度/高度属性与使用高度/宽度 css 设置),您可能会在属性大小规范优先于 css 的情况下产生冲突。

如果您的宽度/高度值被错误地解析并随后被错误地设置,它只会忽略那些 CSS 设置并且大小永远不会改变。

最后,如果您阅读 Lars 链接到的 SO,您会发现进行 svg 缩放的更简单方法是使用 viewbox/preserveaspectration 方法。这是我在为我的东西工作时制作的小提琴:http://jsfiddle.net/reblace/Ku2UQ/2/

var svg = container.selectAll("svg").data([data]);
var svgEnter = svg.enter().append("svg");

// Append a clip path that will hide any data points outside of the xExtent and yExtent
svgEnter
    .attr("viewBox", "0 0 600 200") // This is the "ideal" size of the chart
    .attr("preserveAspectRatio", "xMinYMin");

...

var resize = function(w, h)
    svg.transition().duration(500)
        .attr("width", w)
        .attr("height", w / (width/height));        
;

function doResize() 
    var overflow = $("body").css("overflow");
    $("body").css("overflow", "hidden");

    var width = $("#center").innerWidth();
    var height = $(window).innerHeight() - 10;

    $("body").css("overflow", overflow);
    resize(width, height);
;

var resizeTimer;
$(window).resize(function() 
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(doResize, 250);
);

doResize();

此代码做了几件值得关注的事情,包括过滤调整大小事件,以便它不会为窗口的每个像素大小更改调用调整大小代码。相反,它基本上会缓冲事件,直到您停止调整窗口大小 250 毫秒。

然后,它会隐藏页面上的滚动条,以便在您尝试缩放内容以适应没有滚动条的页面时获得准确的窗口大小。

最后,它缩放 SVG 元素,该元素处理调整内容的大小并将其设置为给定父容器大小的正确大小(根据原始宽度/高度比缩放高度。

【讨论】:

【参考方案3】:

我将 svg 设置为响应式调整大小:

var svg = d3.select("body")
        .append("div")
        .attr("id", "svg")
        .append("svg")
        .attr("viewBox", "0 0 " + width + " " + height )
        .attr("preserveAspectRatio", "xMinYMin");

然后像这样设置css:

#svg 
    width:100%;
    height: auto;

【讨论】:

以上是关于如何使用 d3 创建响应式地图的主要内容,如果未能解决你的问题,请参考以下文章

D3地理节点在地图上绘制?

出于某种原因,我的 D3 地图显示颠倒了 - 我该如何翻转它?

响应式 D3 图表

d3.js 地图:使用 geojson 文件和 CSV 数据

放大和缩小 d3 地图时如何查询数据集?

干货 | Popmotion: 函数式的 JS 动作库;应用程序的运行状况检查;如何正确使用D3属性;提升设计水平的3大法宝