The Angry Robot Zombie Factory launched its second iPhone/iPad app . I haven't mentioned it much yet because I spotted a minor typo in the final version after it had been approved so I submitted an update immediately. To get an early copy (like those misprinted stamps where the plane is upside down), go check out The Elementals. It's free, too. It's a simple, cartoonish periodic table.
Yesterday, the 1k JavaScript demo contest (#js1k) caught my eye. The idea is to create something cool using 1024bytes of JavaScript or less. I rootled around in the middle of The Elementals, grabbed the drawing function and 20 minutes later had made my entry.
The code I submitted is quite minified but isn't obfuscated. When it's unfolded, you can follow the flow fairly easily.
var d = document,
b = d.body,
s = b.style,
w = innerWidth,
h = innerHeight,
v = b.children[0],
p = 2 * Math.PI,
Z = 3,
x = tx = w / 2,
y = ty = h / 2;
The above is a bunch of declarations. Using things like d = document
and b = d.body
allows reuse later on without having to resort to the full document.body.style
and saves a bunch of characters. When you've got such a small amount of space to play with, every character counts (mind you, the ZX81 only had 1k of RAM and look what you could do with that). Now that I'm looking at it, I think I could have tidied this a bit more. Darn. The sneaky bit about this code is the way we grab the reference to the canvas. The code d.getElementById('c')
uses 21 characters but if we look at the provided HTML, we can use the fact that the canvas is the first child of the body element. The code b.children[0]
uses 13 characters instead.
s.margin = "0px";
s.background = "black";
s.overflow = "hidden";
v.width = w;
v.height = h;
t = v.getContext("2d");
This sets the provided canvas to be the full width and height of the window then grabs the drawing context of it so we can make pretty pictures.
zi = function () {
Z++;
Z %= 14
};
m = function (X) {
return (X * 200) % 255
};
Functions to be reused later. zi
increases the number of spinning circles and is used by onmousedown and ontouchstart (oh yes, it works on the iPad, too). m
is a mapping of the index of the circle to a colour. The 200 is arbitrary. I played about a bit until I found some colour combinations I liked.
d.ontouchstart = function (e) {
zi();
tx = e.touches[0].pageX;
ty = e.touches[0].pageY
};
d.onmousemove = function (e) {
tx = e.clientX;
ty = e.clientY
};
d.onmousedown = zi;
Setting the event handlers.
function r() {
t.globalCompositeOperation = 'lighter';
I played about with the various composite operations. Lighter seemed the nicest.
t.clearRect(0, 0, w, h);
t.save();
x = x + (tx - x) / 20;
y = y + (ty - y) / 20;
t.translate(x, y);
Originally, the circles followed the mouse pointer exactly but it lacked any life. By adding in this bit where the movement is delayed as if pulling against friction, it suddenly became a lot more fun and dynamic.
var c = new Date();
for (var i = 1; i <= Z; i++) {
t.fillStyle = 'rgba(' + m(i) * (i % 3) + ', ' + m(i) * ((i + 1) % 3) + ',' + m(i) * ((i + 2) % 3) + ', 0.5)';
t.beginPath();
t.rotate((c.getSeconds() + i) / (i / 4) + (c.getMilliseconds()) / ((i / 4) * 1000));
t.translate(i, 0);
t.arc(-10 - (Z / 5), -10 - +(Z / 5), 100 - (Z * 3), 0, p, false);
t.fill()
}
Erm. Yeah. In essence, all this does is figure out where to draw the circles, how big and what colour. It looks worse than it is. Line-by-line, it translates to:
- Find out the current time
- For each circle we want to draw,
- Pick a colour based on the index of the circle
- Start drawing
- Turn by some amount based on the time and the index
- Move by a small amount based on the index
- Actually draw, making the circles smaller if there are more of them.
- Fill in the circle with the colour.
- Right curly bracket.
t.save();
t.fillStyle = "white";
for (var i = 1; i <= Z; i++) {
t.beginPath();
t.rotate(2);
t.translate(0, 28.5);
t.arc(-120, -120, 5, 0, p, false);
t.fill()
}
t.restore();
t.restore()
}
This does pretty much the same as the one above but always the same size and always the same colour. The t.save
and t.restore
operations throughout mean we can add the transformations onto each other and move stuff relative to other stuff without messing up everything. Goshdarn, that was technical.
setInterval(r, 10);
Kick it all off.
That make sense? Good. Now go make your own js1k entry and submit it. Then download The Elementals. Or Harmonious.