昔、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; }