みかづきブログ・カスタム

基本的にはちょちょいのほいです。

干渉縞を鑑賞するウェブサイトをつくりました 👁️

昔、CodePenにアップした、干渉縞を鑑賞するサイトをリメイクしました。

CodePenでは、base64化した画像を背景画像にしたDOMを回転させていましたが、今回はCanvasで制作したので、動的にパラメータを変更できるようにしています。

DEMO


ソースコード

index.js(抜粋)

const gui = new dat.GUI();
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

let deg = 0;
let speed = .1;
let rectSize = 0;
let punchSize = 2;
let isReverse = false;
let rectCanvas = getRectCanvas(rectSize, punchSize);

gui.domElement.style.position = 'fixed';
gui.domElement.style.top = '0';
gui.domElement.style.right = '-15px';
gui.add({ speed }, 'speed', 0, 1)
    .name('speed')
    .onChange((value) => {
      speed = value;
    });
gui.add({ punchSize }, 'punchSize', 1, 10)
    .name('punch hole size')
    .onChange((value) => {
      punchSize = value;
      rectCanvas = getRectCanvas(rectSize, punchSize);
    });
gui.add({ isReverse }, 'isReverse')
    .name('reverse')
    .onChange((value) => {
      isReverse = value;
    });

    window.addEventListener('resize', handleResize);

handleResize();
render();

function handleResize() {
  rectSize = Math.max(window.innerWidth, window.innerHeight) * 2 * 1.5;
  rectCanvas = getRectCanvas(rectSize, punchSize);
}

function render() {
  canvas.width = window.innerWidth * 2;
  canvas.height = window.innerHeight * 2;

  ctx.drawImage(
    rectCanvas,
    canvas.width / 2 - rectCanvas.width / 2,
    canvas.height / 2 - rectCanvas.height / 2,
    rectCanvas.width,
    rectCanvas.height
  );

  ctx.translate(canvas.width / 2, canvas.height / 2);
  ctx.rotate((deg * Math.PI) / 180);
  ctx.translate(-canvas.width / 2, -canvas.height / 2);

  ctx.drawImage(
    rectCanvas,
    canvas.width / 2 - rectCanvas.width / 2,
    canvas.height / 2 - rectCanvas.height / 2,
    rectCanvas.width,
    rectCanvas.height
  );

  deg = (deg + speed * (isReverse ? -1 : 1)) % 360;
  requestAnimationFrame(render);
}

function getRectCanvas(rectSize, punchSize) {
  const canvas = document.createElement('canvas');

  if (!rectSize || !punchSize) {
    return canvas;
  }

  if (getRectCanvas[rectSize] && getRectCanvas[rectSize][punchSize]) {
    return getRectCanvas[rectSize][punchSize];
  }

  const ctx = canvas.getContext('2d');
  const rowLength = 0 | rectSize / (punchSize * 2 * 2);
  const padding = (rectSize - ((rowLength - 1) * punchSize * 2 * 2 + punchSize * 2)) / 2;

  canvas.width = canvas.height = rectSize;

  for (let i = 0; i < rowLength; i++) {
    for (let j = 0; j < rowLength; j++) {
      ctx.save();
        ctx.beginPath();

        ctx.arc(
          padding + i * punchSize * 2 * 2 + punchSize,
          padding + j * punchSize * 2 * 2 + punchSize,
          punchSize,
          0,
          Math.PI * 2,
          true
        );
        ctx.closePath();
        ctx.fill();
      ctx.restore();
    }
  }

  ctx.save();
    ctx.globalCompositeOperation = 'source-out';
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, rectSize, rectSize);
  ctx.restore();

  getRectCanvas[rectSize] = getRectCanvas[rectSize] || [];
  getRectCanvas[rectSize][punchSize] = canvas;

  return canvas;
}

リポジトリ

github.com