【问题标题】:Trying to make vector map of US counties with d3.js render fast尝试使用 d3.js 快速渲染美国县的矢量图
【发布时间】:2020-05-17 06:20:10
【问题描述】:

我正在尝试在 d3.js 中制作显示美国所有县的 SVG 地图。当您单击某个状态时,它会将viewBox 转换为该状态并根据其在地球上的位置旋转它,因此它不会倾斜。

问题在于它渲染所有路径时非常慢。渲染 SVG 图形这么难有什么原因吗?有什么办法可以解决吗?

如果有人想偷看,我的代码就在这里:

<html>
<head>
    <title>US Map</title>
    <script src="http://d3js.org/d3.v5.min.js"></script>
    <script src="https://d3js.org/topojson.v3.min.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        svg {
            width: 100vw;
            height: 100vh;
        }

        path {
            fill: #ccc;
            stroke: black;
            stroke-width: .2;
        }

        #borders {
            fill: none;
            stroke-width: .8;
        }
    </style>
</head>
<body>
    <svg></svg>
    <script>
        var svg = d3.select("svg"),
        projection = d3.geoAlbersUsa(),
        path = d3.geoPath(projection);

        d3.json("https://cdn.jsdelivr.net/npm/us-atlas@3/counties-10m.json").then(function(us){
            var states = topojson.feature(us, us.objects.states),
            borders = topojson.mesh(us, us.objects.states, (a,b) => a != b);

            states.features.filter(d => ![60,66,69,72,78].includes(Number(d.id))).forEach(function(state){
                svg.datum(state)
                    .append("g")
                    .attr("id", state.id)
                    .on("click", function(d){
                        var p = projection.invert(path.centroid(d));
                        projection.rotate([-p[0], -p[1]]);

                        svg.transition().duration(750)
                            .selectAll("path:not(#borders)")
                            .attr("d", path);

                        var [[x0,y0],[x1,y1]] = path.bounds(d);
                        svg.transition().duration(750)
                            .attr("viewBox", `${x0-5} ${y0-5} ${x1-x0+10} ${y1-y0+10}`)
                            .select("#borders")
                            .attr("d", path(borders));
                    })
                    .selectAll("path")
                    .data(topojson.feature(us, us.objects.counties).features.filter(d => d.id.slice(0,2) == state.id))
                    .enter().append("path")
                    .attr("id", d => d.id)
                    .attr("d", path);
            });

            svg.append("path")
                .attr("id", "borders")
                .attr("d", path(borders));

            var box = svg.node().getBBox();
            svg.attr("viewBox", `${box.x} ${box.y} ${box.width} ${box.height}`);

            projection = d3.geoMercator().scale([1000]);
            path.projection(projection);
        });
    </script>
</body>
</html>```

【问题讨论】:

    标签: javascript html d3.js svg topojson


    【解决方案1】:

    我尝试过不使用 viewBox 的缩放功能。与之前相比,点击转换发生得更快。

    PFA 代码相同。希望这对你有用。

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>D3: Zoom in to reveal counties</title>
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
        <script type="text/javascript" src="https://d3js.org/topojson.v3.min.js"></script>
        <style>
            svg {
                width: 95vw;
                height: 95vh;
            }
            
            .background {
                fill: none;
                pointer-events: all;
            }
            
            #states {
                fill: #aaa;
            }
            
            #states .active {
                display: none;
            }
            
            #state-borders {
                fill: none;
                stroke: black;
                stroke-width: 1.5px;
                stroke-linejoin: round;
                stroke-linecap: round;
                pointer-events: none;
            }
            
            .county-boundary {
                fill: #ccc;
                stroke: black;
                stroke-width: .2px;
            }
            
            .county-boundary:hover,
            .state:hover {
                fill: #ccc;
            }
        </style>
        
    </head>
    
    <body>
        <svg></svg>
    
        <script type="text/javascript">
            var margin = {
                top: 10,
                bottom: 10,
                left: 10,
                right:10
            }, width = parseInt(d3.select('svg').style('width')),
                width = width - margin.left - margin.right,
                mapRatio = 0.45,
                height = width * mapRatio,
                active = d3.select(null);
    
            var svg = d3.select('svg')
                .attr('class', 'center-container')
                .attr('height', height + margin.top + margin.bottom)
                .attr('width', width + margin.left + margin.right);
    
            svg.append('rect')
                .attr('class', 'background center-container')
                .attr('height', height + margin.top + margin.bottom)
                .attr('width', width + margin.left + margin.right)
                .on('click', clicked);
    
    
            Promise.resolve(d3.json("https://cdn.jsdelivr.net/npm/us-atlas@3/counties-10m.json"))
                .then(render);
    
            var projection = d3.geoAlbersUsa()
                .translate([width /2 , height / 2])
                .scale(width);
    
            var path = d3.geoPath()
                .projection(projection);
    
            var g = svg.append("g")
                .attr('class', 'center-container center-items us-state')
                .attr('transform', 'translate('+margin.left+','+margin.top+')')
                .attr('width', width + margin.left + margin.right)
                .attr('height', height + margin.top + margin.bottom)
    
            function render(us) {
    
                g.append("g")
                    .attr("id", "counties")
                    .selectAll("path")
                    .data(topojson.feature(us, us.objects.counties).features)
                    .enter().append("path")
                    .attr("d", path)
                    .attr("class", "county-boundary")
                    .on("click", clicked)
    
                g.append("g")
                    .attr("id", "states")
                    .selectAll("path")
                    .data(topojson.feature(us, us.objects.states).features)
                    .enter().append("path")
                    .attr("d", path)
                    .attr("class", "state active")
                    .on("click", clicked);
                
                g.append("path")
                    .datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
                    .attr("id", "state-borders")
                    .attr("d", path);
    
            }
    
            function clicked(d) {
                if (d3.select('.background').node() === this) return reset();
    
                if (active.node() === this) return reset();
    
                active.classed("active", false);
                active = d3.select(this).classed("active", true);
    
                var bounds = path.bounds(d),
                    dx = bounds[1][0] - bounds[0][0],
                    dy = bounds[1][1] - bounds[0][1],
                    x = (bounds[0][0] + bounds[1][0]) / 2,
                    y = (bounds[0][1] + bounds[1][1]) / 2,
                    scale = .1 / Math.max(dx / width, dy / height),
                    translate = [width / 2 - scale * x, height / 2 - scale * y];
    
                g.transition()
                    .duration(750)
                    .style("stroke-width", 1.5 / scale + "px")
                    .attr("transform", "translate(" + translate + ")scale(" + scale + ")");
            }
    
    
            function reset() {
                active.classed("active", false);
                active = d3.select(null);
    
                g.transition()
                    .delay(100)
                    .duration(750)
                    .style("stroke-width", "1.5px")
                    .attr('transform', 'translate('+margin.left+','+margin.top+')');
            }
        </script>
    </body>
    
    </html>

    【讨论】:

      猜你喜欢
      • 2021-11-05
      • 2013-09-29
      • 1970-01-01
      • 1970-01-01
      • 2020-07-05
      • 1970-01-01
      • 2014-01-19
      • 2016-06-30
      • 2021-06-11
      相关资源
      最近更新 更多