<div>
<h3>Mori Painter</h3>
</div>
<div id='container'>
<canvas id='canvas'></canvas>
</div>
<div>
<button id='undo'>↶</button>
</div>
body {
font-family: monospace;
}
canvas {
position: relative;
}
#container{
width: 200px;
height: 200px;
border: solid 5px #000;
}
const {
list, vector, peek, pop, conj, map, assoc, zipmap,
range, repeat, each, intoArray, toJs, isEmpty,
count
} = mori;
const log = (...args) => {
console.log(...args.map(toJs));
};
// the dimensions of the canvas
const [height, width] = [20, 20];
// the size of each canvas pixel
const pixelSize = 10;
// converts an integer to a 2d coordinate
const to2D = (i) => vector(
i % width,
Math.floor(i / width)
);
// sequence of coordinates in the canvas
const coords = map(to2D, range(height * width));
// lazy sequence of colors
const colors = repeat('#fff');
// colors stored against coordinates
const pixels = zipmap(coords, colors);
// update pixels to reflect drawing at x, y
const draw = (x, y, pixels, color='#000') => {
const coord = vector(x, y);
return assoc(pixels, coord, color);
};
// paint the values in pixels onto the ctx
const paint = (ctx, pixels) => {
const px = pixelSize;
each(pixels, p => {
const [coord, color] = intoArray(p);
const [x, y] = intoArray(coord);
ctx.fillStyle = color;
ctx.fillRect(x * px, y * px, px, px);
});
};
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const undo = document.getElementById('undo');
canvas.width = width * pixelSize;
canvas.height = height * pixelSize;
paint(context, pixels);
// keep track of current frames with rebindable variable
let frames = list(pixels);
canvas.addEventListener('click', e => {
const x = Math.floor(e.layerX / pixelSize);
const y = Math.floor(e.layerY / pixelSize);
const pixels = draw(x, y, peek(frames));
frames = conj(frames, pixels);
paint(context, pixels)
});
undo.addEventListener('click', e => {
if(count(frames) > 1) {
frames = pop(frames);
paint(context, peek(frames));
}
});
Also see: Tab Triggers