试图使用d3.js快速绘制美国各县的矢量地图

问题描述 投票:1回答:1

我正在尝试在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个回答
0
投票

我尝试了不使用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>
© www.soinside.com 2019 - 2024. All rights reserved.