<!doctype html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<canvas id="bg" width="500" height="500"></canvas>
<canvas id="fg" width="500" height="500"></canvas>
<div class="controls">
<form>
<fieldset>
<legend>Shape</legend>
<label><input type="radio" name="shape" value="triangle"> Triangle</label>
<label><input type="radio" name="shape" value="circle"> Circle</label>
<label><input type="radio" name="shape" value="square"> Square</label>
</fieldset>
<label>
Shape Size: <input name="size" type="number" min="3" max="20">px
</label>
</form>
</div>
</body>
</html>
// Size of each side of the shape in pixels.
var size = 8;
// Height of the shape in pixels;
var height = 0;
// Either 'triangle' or 'square';
var shape = 'triangle';
// Handles to DOM elements.
var bg, fg, form, shapeField, sizeField;
// How far the foreground has been rotates in degrees;
var degrees = 0;
// Whether the mask is auto-animating
var animating = false;
// Set the canvas to fill the screen.
function resizeCanvas(el) {
if (window.innerWidth > window.innerHeight) {
el.width = el.height = window.innerWidth;
} else {
el.width = el.height = window.innerHeight;
}
el.style.marginTop = '-' + (el.height / 2) + 'px';
el.style.marginLeft = '-' + (el.width / 2) + 'px';
fillShape(el);
}
// Draws a single shape to a drawing context at a given coordinate.
function drawShape(ctx, x, y) {
ctx.beginPath();
ctx.moveTo(x, y);
if (shape === 'circle') {
ctx.arc(x, y, size/2, 0, 2 * Math.PI, false);
} else {
ctx.lineTo(x + size, y);
if (shape === 'triangle') {
ctx.lineTo(x + (size / 2), y - height);
} else {
// shape === 'square'
ctx.lineTo(x + size, y + size);
ctx.lineTo(x, y + size);
}
}
ctx.fill();
}
// Fills the canvas with a particular shape.
function fillShape(canvas) {
var ctx = canvas.getContext('2d');
var shift = false;
var vskip, hskip;
if (shape === 'triangle') {
vskip = size;
hskip = size / 2;
} else {
vskip = size * 2;
hskip = size;
}
for (var j = 0; j < canvas.height + size; j += size) {
for (var i = 0; i < canvas.width + size; i += vskip) {
if (shift) {
drawShape(ctx, i - hskip, j);
} else {
drawShape(ctx, i, j);
}
}
shift = !shift;
}
}
function setRotation(el, deg) {
el.style.transform = 'rotateZ(' + deg + 'deg)';
}
function animate(el) {
var lastTimestamp = performance.now();
function doAnimate(timestamp) {
if (!animating) {
return;
}
setRotation(el, degrees);
degrees += (timestamp - lastTimestamp) / 50;
lastTimestamp = timestamp;
requestAnimationFrame(doAnimate);
}
doAnimate(performance.now());
}
function onResize() {
if (shape === 'triangle') {
height = (Math.sqrt(3) / 2) * size;
} else {
height = size;
}
resizeCanvas(bg);
resizeCanvas(fg);
}
function onClick() {
animating = !animating;
if (animating) {
animate(fg);
}
}
function onMove(evt) {
if (animating) {
return;
}
var cx = window.innerWidth / 2,
cy = window.innerHeight / 2,
rad = Math.atan2(evt.clientY - cy, evt.clientX - cx);
degrees = rad * 180 / Math.PI;
setRotation(fg, degrees);
}
function onInput() {
size = parseInt(sizeField.value, 10);
shape = shapeField.value;
onResize();
}
function main() {
bg = document.getElementById('bg');
fg = document.getElementById('fg');
form = document.forms[0];
sizeField = form.elements.size;
shapeField = form.elements.shape;
var bgCtx = bg.getContext('2d'),
fgCtx = fg.getContext('2d');
bgCtx.strokeStyle = bgCtx.fillStyle =
fgCtx.strokeStyle = fgCtx.fillStyle = 'black';
onResize();
sizeField.value = size;
shapeField.value = shape;
// Event Listners
window.addEventListener('resize', onResize);
bg.addEventListener('click', onClick);
fg.addEventListener('click', onClick);
document.addEventListener('mousemove', onMove);
document.addEventListener('touchmove', function(evt) {
onMove(evt.touches.item(0));
});
document.forms[0].addEventListener('submit', function (evt) {
evt.preventDefault();
});
sizeField.addEventListener('input', onInput);
for (var i = 0; i < shapeField.length; i++) {
shapeField.item(i).addEventListener('click', onInput);
}
}
// Wait for the page to be finished loading
if (document.readyState !== 'loading') {
main();
} else {
document.addEventListener('DOMContentLoaded', main);
}
body {
background: white;
overflow: hidden;
}
canvas {
position: fixed;
top: 50%;
left: 50%;
}
.controls {
background-color: lightblue;
position: fixed;
z-index: 10;
}
#sizeField {
width: 50px;
}
Inspired by: https://www.reddit.com/r/math/comments/41kgud/moir%C3%A9_pattern/
Click to toggle animation.