Search code examples
htmlsvgsvg-pattern

SVG: generate increasing numbers on grid


So basicly I have a grid that is using parrerns of paths/rects to draw the grid. On this grid I want to put numbers in x & y direction. Like a coordinate system you might know from school.

I tried putting text in the patterns but there are two problems with that:

  • the text in every rectangle is the same(not iterating)
  • every rectangle contains text(instead of just the "axes")

My guess is that I need another svg that will go on top of the other one in order to fix the last issue.

These are my patterns:

<defs>
    <pattern id="smallGrid" width="15px" height="15px" patternUnits="userSpaceOnUse">
        <path d="M 15 0 L 0 0 0 15" fill="none" stroke="gray" stroke-width="0.5"/>
    </pattern>
    <pattern id="grid" width="75" height="75" patternUnits="userSpaceOnUse">
        <text x="20" y="70">foo</text> <== Here is the text I inserted
        <rect width="75" height="75" fill="url(#smallGrid)"/>
        <path d="M 75 0 L 0 0 0 75" fill="none" stroke="blue" stroke-width="4"/>
    </pattern>
</defs>

The patterns get placed in an <svg>-tag refering to their id like this:

<svg>
    <g id="viewport">
        <rect id="gridParent" fill="url(#grid)"/>
    </g>
</svg>

The end result looks smth like this: Wrong text

But I want to do something like this: Right text

I am thankful for all the hints you can give to me!!! Thanks in advance.


Solution

  • As @Robert Longson already pointed out:
    svg <pattern> is rather static – so you can't implement any dynamic counter.

    If you don't need to highlight individual columns (e.g with different fill colors) you could append <text> elements with some javaScript.

    Essentially you need to loop through all columns/grid cells defined by your grids width and height and add <text> elements with appropriate x and y coordinates.

    In this case:
    (width/height) 600x300 = 8 columns; 4rows; 32 grid cells

    Each time to reach the iteration index is divisible by the column count, the current y offset is incremented according to the total numbers of rows.

    Example: Add labels to svg grid

    const svg = document.querySelector('svg');
    const bb = svg.getBBox();
    const strokeWidth = 4;
    
    //get viewBox and adjust it to avoid overflow caused by grid strokes
    const [x, y, width, height] = [bb.x, bb.y, bb.width + strokeWidth / 2, bb.height + strokeWidth / 2];
    svg.setAttribute('viewBox', [x, y, width, height].join(' '));
    const cols = Math.floor((width - strokeWidth / 2) / 75);
    const rows = Math.floor((height - strokeWidth / 2) / 75);
    
    // add labels
    addGridLabelsCoord(svg, cols, rows, width, height, strokeWidth);
    
    
    function addGridLabelsCoord(svg, cols, rows, width = 1, height = 1, strokeWidth = 1) {
      // set initial y/x offset according to stroke width to avoid cropped outer strokes
      let offsetX = (width - strokeWidth / 2) / cols;
      let offsetY = (height - strokeWidth / 2) / rows;
      let currentRow = 1;
      let currentCol = 1;
      let shiftX = 0;
      let shiftY = 0;
      let cellCount = cols * rows;
      let nameSpace = 'http://www.w3.org/2000/svg';
    
      // loop through all columns
      for (let i = 0; i < cellCount; i++) {
        // if current index is divisible by columns – move to next row
        if (i > 0 && i % (cols) === 0) {
          shiftX = 0;
          shiftY += offsetY;
          currentCol = 1;
          currentRow++;
        }
         // add labels only for first row and first columns
        if (currentRow == 1 || currentCol == 1) {
          let colLabel = currentCol == 1 ? currentRow - 1 : i;
          // add new cell to output
          let text = document.createElementNS(nameSpace, 'text');
          text.setAttribute('x', +(shiftX + offsetX / 2).toFixed(1));
          text.setAttribute('y', +(shiftY + offsetY / 2).toFixed(1));
          text.setAttribute('dominant-baseline', 'central');
          text.setAttribute('text-anchor', 'middle');
          text.textContent = colLabel;
          svg.appendChild(text);
        }
    
        // increment x offset for next column
        shiftX += offsetX;
        currentCol++;
      }
    }
    <svg viewBox="0 0 600 300">
            <defs>
                <pattern id="smallGrid" width="15px" height="15px" patternUnits="userSpaceOnUse">
                    <path d="M 15 0 L 0 0 0 15" fill="none" stroke="gray" stroke-width="0.5" />
                </pattern>
                <pattern id="grid" width="75" height="75" patternUnits="userSpaceOnUse">
                    <rect width="75" height="75" fill="url(#smallGrid)" />
                    <path d="M 75 0 L 0 0 0 75" fill="none" stroke="blue" stroke-width="4" />
                </pattern>
            </defs>
            <rect x="0" y="0" width="100%" height="100%" id="gridParent" fill="url(#grid)" />
        </svg>