Jenny
Jenny

Reputation: 13

How to repeat a HTML Canvas pattern diagonally

I've created a canvas pattern and want to repeat the pattern diagonally through the whole web page. Since the repeat property only support repeat-x, repeat-y or repeat both direction, I've set it to 'no-repeat' for now and tried to use offset or translate to move my pattern diagonally, but didn't work out.

Here is what I got for now:

enter image description here

Here is what I want to accomplish: enter image description here

I'm just trying to mimic the effect, doesn't need to be exact the same.

Can someone tell me how to continue my pattern diagonally? Thanks a lot!

Here are some of my codes:

      var patternCanvas = document.createElement('canvas');
			var patternContext = patternCanvas.getContext('2d');
			patternCanvas.width = 350;
			patternCanvas.height = 350;

			patternContext.fillStyle = "orange";
			patternContext.fillRect(0, 50, 150, 50);
			patternContext.fillRect(50, 0, 50, 150);

			patternContext.fillStyle = "black";
			patternContext.fillRect(100, 100, 150, 50);
			patternContext.fillRect(150, 50, 50, 150);

			patternContext.fillStyle = "green";
			patternContext.fillRect(200, 150, 150, 50);
			patternContext.fillRect(250, 100, 50, 150);

			patternContext.fillStyle = "darkred";
			patternContext.fillRect(0, 100, 50, 150);
			patternContext.fillRect(0, 150, 150, 50);

			patternContext.fillStyle = "blue";
			patternContext.fillRect(100, 150, 50, 150);
			patternContext.fillRect(50, 200, 150, 50);

			patternContext.fillStyle = "yellow";
			patternContext.fillRect(200, 200, 50, 150);
			patternContext.fillRect(150, 250, 150, 50);
		
			var canvas = document.getElementById("myCanvas");
			canvas.width = window.innerWidth;
			canvas.height = window.innerHeight;
			var context = canvas.getContext('2d');

			var pattern = context.createPattern(patternCanvas, 'no-repeat');
			context.fillStyle = pattern;
			context.fillRect(0, 0, 350, 350);
<canvas id="myCanvas"></canvas>

Upvotes: 1

Views: 591

Answers (2)

Cat
Cat

Reputation: 4246

This solution treats the canvas as a blank slate rather than as a place to copy/paste a pattern.

It creates the appearance of a repeating pattern by dividing the canvas into squares and coloring each square according to its position in a virtual grid.

Thanks to irchans for help with the math.

const
  // Creates a canvas, and a context
  canvas = document.getElementById("myCanvas"),
  context = canvas.getContext('2d'),

  // Configures colors, unit-square size, and the number of unit squares to draw
  colors = "blue,yellow,darkred,green,orange,black".split(","),
  unit = 50,
  gridDimensionX = 10,
  gridDimensionY = 10;

// Makes the canvas wide enough for its content
canvas.width = unit * gridDimensionX;
canvas.height = unit * gridDimensionY;

// Builds a grid of squares, each of which is assigned a color
const grid = makeGrid(gridDimensionX, gridDimensionY, colors);

// Loops through the grid and draws each square
drawGrid(grid, context, unit);


// Defines the `makeGrid` function
function makeGrid(gridDimensionX, gridDimensionY, colors){
  const grid = [];
  for(let y = 0; y < gridDimensionY; y++){
    const row = [];
    for(let x = 0; x < gridDimensionX; x++){
      
      // Assigns coordinates to each not-yet-drawn square, along two axes 
      //   (rotated 60 degrees from the vertical and horizontal axes)
      //   and groups squares according to these coordinates
      cell = {
        slantyRowGrp: Math.round((2 * y - x) / 5, 0),
        slantyColGrp: Math.round((y + 2 * x) / 5, 0)
      }
      
      // Assigns a color to each square based on its 'slanty' grouping
      cell.colorIndex = (cell.slantyRowGrp + 2 * cell.slantyColGrp) % colors.length;
      cell.color = colors[cell.colorIndex];

      // Adds the cell to the row
      row.push(cell);
    }

    // Adds the completed row to the grid
    grid.push(row);
  }
  // Returns the completed grid
  return grid;
}

// Defines the `drawGrid` function
function drawGrid(grid, context, unit){
  grid.forEach( (row, y) => {
    row.forEach( (cell, x) => {

      // Fills each square with its assigned color
      context.fillStyle = cell.color;
      context.fillRect(unit * x, unit * y, unit, unit);
      
      // Displays the 'slanty' row and column group numbers
      /*
      context.fillStyle = "lightgrey";
      context.fillText(
        `${cell.slantyRowGrp}; ${cell.slantyColGrp}`,
        unit * x + unit/2.5,
        unit * y + unit/2
      );
      */
    });
  });
}
<canvas id="myCanvas"></canvas>

Upvotes: 1

George
George

Reputation: 2422

It took quite a lot of effort to achieve. It's a deceptively complex question.

Each position moves by something like x = x + (iterationY/3) * 2, y = iterationX although my code has denormalized iteration steps for ix and iy of 1/3, to make it easier to reason about moving by cross blocks e.g. 1/3 width or height of a cross.

To assign an id to each cross for colouring, I take a row and column where x/y iterations have a step of 1 as row = iterationX % 2 and col = iterationY % 3, which gives row going from 0, 1 and so on, and col going from 0, 1, 2 and repeating. In this case I assign col a weight of 2, so the id = row+(col*2), to ensure each id is unique. Finally, I define an array of colours which can be referenced by this id.

const canvas = document.getElementById("canv");
const ctx = canvas.getContext('2d');
const w = window.innerWidth;
const h = window.innerHeight;
const s = 80;//size
const bs = s / 3;//block size
const gs = Math.max(w, h)/s;//grid size
canvas.width = w;
canvas.height = h;
let yAcc = 0;
let jx = 0;
let jy = 0;
const getColour = (jx, jy) => {
  const row = (jx%2);//0, 1
  const col = (jy % 3);//0, 1, 2
  //00, 10, 01, 11, 02, 12
  //0 , 1 , 2 , 3 , 4 , 5
  const id = row + (col*2);
  const colours = ['orange', 'blue', 'black', 'yellow', 'green', 'red']
  return colours[id];
}
for(let ix = 0; ix < gs; ix+=1/3){
  
  for(let iy = 0; iy < gs; iy+=1/3){
    const colour = getColour(jx, jy);
    let x = ix+iy*2-(gs);
    let y = iy+ix*3-(gs);
    ctx.fillStyle = colour;
    ctx.beginPath();
    ctx.lineWidth = 2;
    ctx.rect(x * bs * 3, y * bs * 3 + bs, bs * 3, bs);
    
    ctx.rect(x * bs * 3 + bs, y * bs * 3, bs, bs * 3);
    ctx.fill();
    ctx.closePath();
    jy ++;
  }
  jx ++;
}
<canvas id="canv"></canvas>

Upvotes: 1

Related Questions