如何使用缩放和平移在 d3.js 中绘制网格线

How do I draw gridlines in d3.js with zoom and pan

我已经能够制作具有缩放和平移功能的散点图,其中轴可以正确缩放并且一切正常。现在我想弄清楚如何添加网格线,但是 运行 遇到了一些问题。我开始只添加 x 轴网格线来解决问题。我附上了一个 fiddle 和一个可以构建的工作示例。

我在生成图表时注释掉了初始网格线,因为它们在缩放后会保留下来导致混乱,稍后我会在工作正常时将它们添加回来。缩放时网格线似乎正确绘制,但它们与 x 轴标签不匹配,并且 x 轴标签在缩放或平移后消失。

如果您注释掉第 163 行并取消注释第 164 行,您可以看到没有任何网格线的基本图形。单击绘图按钮将始终生成一个新图形。我留下了一些注释掉的代码,这些代码是我尝试通过 Whosebug 搜索的不同内容。

示例使用 d3.js - 5.9.2

JSFiddle:https://jsfiddle.net/eysLvqkh/11/

HTML:

<div id="reg_plot"></div>
<button id="b" class="myButton">plot</button>

Javascript:

var theButton = document.getElementById("b");
theButton.onclick = createSvg;

function createSvg() {
                        // clear old chart when 'plot' is clicked
                        document.getElementById('reg_plot').innerHTML = ""
                        // dimensions
            var margin = {top: 20, right: 20, bottom: 30, left: 55},
                svg_dx = 1200, 
                svg_dy =600,
                chart_dx = svg_dx - margin.right - margin.left,
                chart_dy = svg_dy - margin.top - margin.bottom;

            // data
            var y = d3.randomNormal(400, 100);
            var x_jitter = d3.randomUniform(-100, 1400);

            var d = d3.range(1000)
                        .map(function() { 
                            return [x_jitter(), y()]; 
                        });

            // fill
            var colorScale = d3.scaleLinear()
                                .domain(d3.extent(d, function(d) { return d[1]; }))
                                .range([0, 1]);


            // y position
            var yScale = d3.scaleLinear()
                            .domain(d3.extent(d, function(d) { return d[1]; }))
                            .range([chart_dy, margin.top]);
            
            // x position
            var xScale = d3.scaleLinear()
                            .domain(d3.extent(d, function(d) { return d[0]; }))
                            .range([margin.right, chart_dx]);

            // y-axis
            var yAxis = d3.axisLeft(yScale);

            // x-axis
            var xAxis = d3.axisBottom(xScale);

            // append svg to div element 'reg_plot' and set zoom to our function named 'zoom'
            var svg = d3.select("#reg_plot")
                        .append("svg")
                        .attr("width", svg_dx)
                        .attr("height", svg_dy);
            svg.call(d3.zoom().on("zoom", zoom));

            // clip path - sets boundaries so points will not show outside of the axes when zooming/panning
            var clip = svg.append("defs").append("svg:clipPath")
            .attr("id", "clip")
            .append("svg:rect")
            .attr("id", "clip-rect")
            .attr("x", "0")
            .attr("y", "0")
            .attr('width', chart_dx)
            .attr('height', chart_dy);

            // plot data
            var circles = svg.append("g")
                            .attr("id", "circles")
                            .attr("transform", "translate(75, 0)")
                            .attr("clip-path", "url(#clip)")
                            .selectAll("circle")
                            .data(d)
                            .enter()
                            .append("circle")
                            .attr("r", 4)
                            .attr("cx", function(d) { return xScale(d[0]); })
                            .attr("cy", function(d) { return yScale(d[1]); })
                            .style("fill", function(d) { 
                                var norm_color = colorScale(d[1]);
                                return d3.interpolateInferno(norm_color) 
                            });

            // add y-axis
            var y_axis = svg.append("g")
                            .attr("id", "y_axis")
                            .attr("transform", "translate(75,0)")
                            .call(yAxis).style("font-size", "10px")
                        
            // add x-axis
            var x_axis = svg.append("g")
                            .attr("id", "x_axis")
                            .attr("transform", `translate(${margin.left}, ${svg_dy - margin.bottom - margin.top})`)
                            .call(xAxis).style("font-size", "10px")
            
            // add x and y grid lines
            x_axis.call(xAxis.scale(xScale).ticks(20).tickSize(-chart_dy));
            y_axis.call(yAxis.scale(yScale).ticks(20).tickSize(-chart_dx));

            function zoom(e) {
                // re-scale y axis during zoom
                y_axis.transition()
                        .duration(50)
                        .call(yAxis.scale(d3.event.transform.rescaleY(yScale)));

                // re-scale x axis during zoom
                x_axis.transition()
                        .duration(50)
                        .call(xAxis.scale(d3.event.transform.rescaleX(xScale)));

                // re-draw circles using new scales
                var new_xScale = d3.event.transform.rescaleX(xScale);
                var new_yScale = d3.event.transform.rescaleY(yScale);

                // re-scale axes and gridlines
                x_axis.call(xAxis.scale(new_xScale).ticks(20).tickSize(-chart_dy));
                y_axis.call(yAxis.scale(new_yScale).ticks(20).tickSize(-chart_dx));
                circles.data(d)
                    .attr('cx', function(d) {return new_xScale(d[0])})
                    .attr('cy', function(d) {return new_yScale(d[1])});                
            }

        }
             

对于任何正在寻找的人,我已经解决了这个问题。我已经更新了原来的post中的javascript,并更新了jsfiddle。如果您将此代码复制到您使用 d3.js 7.4.4 或更高版本的本地计算机,那么您需要将 d3.event.transform... 的行更改为 e.transform。