Search code examples
javascriptfabricjs

Is there a more efficient way of creating a Grid in Canvas?


I am creating a grid over an image using canvas and such i created what i have in my below jsfiddle.

http://jsfiddle.net/3bufekmh/1/

as you can see from the example i am using huge amounts of:

canvas.add(new fabric.Rect({});

to achieve a grid like system. I feel like there should be an easier way with less code?


Solution

  • There a few ways you could handle this - perhaps this is an acceptable solution. Really it depends on how flexible you want the grid to be...

    const canvas = new fabric.Canvas('c', {
      selection: false
    });
    
    // size of squares
    const size = 25;
    
    // starting offsets
    const offset = {
      left: 49,
      top: 50,
    };
    
    // where "1" represents a square and "0" a gap
    const grid = [
      [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1],
      [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
      [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 
    ];
    
    // draws a square at the given grid coordinate
    function drawSquare(x, y) {
      canvas.add(new fabric.Rect({
        left: x * size + offset.left,
        top: y * size + offset.top,
        width: size,
        height: size,
        fill: 'rgba(0,0,0,0)',
        originX: 'left',
        originY: 'top',
        centeredRotation: true,
        stroke: 'black',
        strokeWidth: 1
      }));
    }
    
    // loop over our grid rows and cells...
    for (const [y, row] of grid.entries()) {
      for (const [x, cell] of row.entries()) {
        // draw a square if the cell value is 1 (true)
        cell && drawSquare(x, y);
      }
    }
    canvas {
        border: 1px solid #ccc;
    }
    <script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
    <canvas id="c" width="550" height="250"></canvas>

    If you wanted to be super concise and have a "less editable" grid then you can store the rows as numbers - and then just use their bits as flags...although it is probably also less readable. e.g.

    const canvas = new fabric.Canvas('c', {
      selection: false
    });
    
    const config = {
      grid: [0x7C01F, 0x7E1FF, 0x7FFFF, 0x7FFFF, 0x7FFFF, 0x7FFFF, 0x3FFF],
      size: 25,
      offset: {
        top: 49,
        left: 50
      }
    };
    
    function drawSquare(x, y) {
      canvas.add(new fabric.Rect({
        left: x * config.size + config.offset.left,
        top: y * config.size + config.offset.top,
        width: config.size,
        height: config.size,
        fill: 'rgba(0,0,0,0)',
        originX: 'left',
        originY: 'top',
        centeredRotation: true,
        stroke: 'black',
        strokeWidth: 1
      }));
    }
    
    function h2b(h) {
      return h.toString(2).padStart(19, '0').split('').entries();
    }
    
    for (const [y, row] of config.grid.entries()) {
      for (const [x, cell] of h2b(row)) {
        +cell && drawSquare(x, y);
      }
    }
    canvas {
        border: 1px solid #ccc;
    }
    <script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
    <canvas id="c" width="550" height="250"></canvas>

    ..and if you wanted to be super-duper concise and you don't care at all about readability or maintainability!

    const g = new fabric.Canvas('c', {
      selection: false
    });
    
    [0x7C01F, 0x7E1FF, 0x7FFFF, 0x7FFFF, 0x7FFFF, 0x7FFFF, 0x3FFF].forEach(
      (r, i) => r.toString(2).padStart(19, 0).split('').forEach(
        (c, j) => +c && g.add(new fabric.Rect({
          left: j * 25 + 50,
          top: i * 25 + 49,
          width: 25,
          height: 25,
          fill: 'rgba(0,0,0,0)',
          stroke: 'black'
        }))));
    canvas {
        border: 1px solid #ccc;
    }
    <script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
    <canvas id="c" width="550" height="250"></canvas>