Skip to content

Instantly share code, notes, and snippets.

@leonardfischer
Last active December 16, 2021 22:27
Show Gist options
  • Save leonardfischer/fc4d1086c64b2c1324c93dcd0beed004 to your computer and use it in GitHub Desktop.
Save leonardfischer/fc4d1086c64b2c1324c93dcd0beed004 to your computer and use it in GitHub Desktop.
Creating a zoom- and panable tile-grid in D3 (via SVG patterns)

Creating a zoom- and panable tile-grid in D3 (via SVG patterns)

Instead of rendering huge amount of lines on the X and Y axis, we can simply make use of the SVG "pattern" feature :)

See it in action here

I found the original code here, but it was mixed in Vue syntax. This gist simply contains the 'raw' example of the grid related stuff :)

Problem with "partially rendered" grid?

This can happen when the grid size grows larger then the SVG itself (in this example if the grid exceeds a width of 640 or a height of 480 pixel). You'll simply have to extend the SVG to a certain size and maybe hide it (in case your application has limited space) via style="overflow:hidden".

<svg>
<defs>
<pattern id="inner-grid" width="10" height="10" patternUnits="userSpaceOnUse">
<rect width="100%" height="100%" fill="none" stroke="#ccc" stroke-width="0.5" />
</pattern>
<pattern id="grid" width="100" height="100" patternUnits="userSpaceOnUse">
<rect width="100%" height="100%" fill="url(#inner-grid)" stroke="#ccc" stroke-width="1.5" />
</pattern>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="url(#grid)"></rect>
<g id="canvas">
<!-- This will be your canvas -->
<circle class="zero" r="5"></circle>
</g>
</svg>
// !! Remember to include D3 v5 !!
// Define a width and height.
const width = 640;
const height = 480;
// Prepare the SVG.
const svg = d3.select('svg')
.attr('width', width)
.attr('height', height)
.attr('viewBox', [0, 0, width, height]);
// Select the grid, inner grid and canvas (so we can use them when we pan and zoom).
const patternGrid = svg.select('#grid');
const patternInnerGrid = svg.select('#inner-grid');
const canvas = svg.select('g');
// The main 'magic' happenes in our zoom callback method.
const zoomed = (transform) => {
const transform10 = parseInt(transform.k * 10);
const transform100 = transform10 * 10;
// Don't move the grid itself, simply change the pattern.
patternGrid
.attr('x', parseInt(transform.x % transform100))
.attr('y', parseInt(transform.y % transform100))
.attr('width', transform100)
.attr('height', transform100);
patternInnerGrid
.attr('width', transform10)
.attr('height', transform10);
// Translate and scale the canvas.
canvas.attr('transform', transform);
};
svg.call(d3.zoom()
.extent([[0, 0], [width, height]])
.scaleExtent([1, 10])
.on("zoom", () => zoomed(d3.event.transform)));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment