Home > Back-end >  How do I draw gridlines in d3.js with zoom and pan
How do I draw gridlines in d3.js with zoom and pan

Time:05-24

I have been able to make a scatter plot with zoom and pan functionality where the axes scale properly and everything works well. Now I am trying to figure out how to add gridlines, but running into some issues. I have started with only adding x-axis gridlines to figure things out. I have attached a fiddle with a working example to build from.

I commented out the initial gridlines when the graph is generated, because they would remain after zooming causing clutter, and I will add them back later when I get things working. When zooming the gridlines appear to be drawn correctly, but they do not match up with the x-axis labels, and the x-axis labels disappear after zooming or panning.

If you comment out line 163 and uncomment line 164 you can see the basic graph without any gridlines. Clicking the plot button will always generate a new graph. I have left behind some commented out code of different things that I have tried from searching through stackoverflow.

Example is using d3.js - 5.9.2

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

HTML:

<div id="reg_plot"></div>
<button id="b" >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])});                
            }

        }
             

CodePudding user response:

For anyone looking, I have solved this problem. I have updated the javascript in the original post, and updated the jsfiddle. If you are copying this code to your local machine where you are using d3.js 7.4.4 or higher then you need to change the lines that say d3.event.transform.... to just e.transform.

  • Related