背景の中で何種類かのパターンを繰り返して表示する

背景の中で何種類かのパターンを繰り返して表示する

全然書かなくなるということになったので、たまたま業務でやったやつを残しておく。
表題がめちゃくちゃわかりにくいけど、要はこんな感じのやつ。

例えば500 * 1000のパターンを4種類繰り返して表示するみたいなこと。

実装

CSSではできないのでcanvasを背面に敷きましょう。

HTML & CSS

<div class="wrapper" id="js-wrapper">
 <canvas id="c"></canvas>
 <div class="contents">
  ここに内容を書く
 </div>
</div>
.wrapper {
  position: relative
}

canvas {
  position: absolute;
  top: 0;
  left: 0
}

.contents {
  position: relative
}

そういうわけでcanvasです。


const c = document.getElementById('c');
const ctx = c.getContext('2d');
const PATTERN_HEIGHT = 1000;

let w;
let h;

// 描画のサイズが知りたいので親要素も取得する。
const wrapper = document.getElementById('js-wrapper');

const init = () => {
  w = c.width = wrapper.offsetWidth;
  h = c.height = wrapper.offsetHeight;
}
init();

// パターン画像の種類
const PATTERN_1 = 'PATTERN_1';
const PATTERN_2 = 'PATTERN_2';
const PATTERN_3 = 'PATTERN_3';

// パターンのサイズを柔軟にしたいのでpatternObj.imgにはimgではなくcanvasを格納する
const patternSource = [
  {
    name: PATTERN_1,
    url: '/assets/img/pattern_1.png',
    img: document.createElement('canvas')
  },
  {
    name: PATTERN_2,
    url: '/assets/img/pattern_2.png',
    img: document.createElement('canvas')
  },
  {
    name: PATTERN_3,
    url: '/assets/img/pattern_3.png',
    img: document.createElement('canvas')
  },
];

// パターンの順番
const patternOrder = [
  PATTERN_2,
  PATTERN_3,
  PATTERN_2,
  PATTERN_1,
];

// イメージをロードする
const loadImage = patternObj => new Promise(resolve => {
  const img = new Image();
  img.src = patternObj.url;
  img.onload = () => {
    // canvasにimgを任意のサイズで描画する。
    const imgWidth = patternObj.img.width = img.width;
    const imgHeight = patternObj.img.height = img.height;
    const imgCtx = patternObj.img.getContext('2d');
    imgCtx.drawImage(img, 0, 0, imgWidth, imgHeight);
    resolve()
  }
});

// imageリソースを全て取得してからコールバックを実行
const loadImages = async (patternSource, callback) => {
  await Promise.all(
    patternSource.map(
      pattern => loadImage(pattern)
    )
  )

  callback();
};

const drawPattern = (
  ctx,
  startX, startY,
  width, height,
  maxY,
  patternSource,
  patternOrder
) => {
  let i = 0;
  let drawHeight = startY;
  while(drawHeight < maxY) {
    const source = patternSource.filter(source => source.name === patternOrder[i % patternOrder.length])[0].img;

    const pattern = ctx.createPattern(source, 'repeat');
    ctx.fillStyle = pattern;
    ctx.fillRect(startX, startY + height * i, width, height);
    i++;
    drawHeight += height;
  }
}

const draw = () => {
  init();
  ctx.clearRect(0, 0, w, h);
  // 左右ハーフステップで繰り返させる
  drawPattern(
    ctx,
    0, 0,
    w / 2, PATTERN_HEIGHT,
    h,
    patternSource,
    patternOrder
  )
  drawPattern(
    ctx,
    w / 2, -PATTERN_HEIGHT / 2,
    w / 2, PATTERN_HEIGHT,
    h,
    patternSource,
    patternOrder
  )
}

ctx.beginPath()
loadImages(patternSource, draw)

だるい

はい。