An animated version of one of my favorite childhood toys. Use arrow keys and Home/End to change the drawing parameters. Aside from how much I loved playing with these, the theme is reflected in the d…
f='(`(u,I_nUPI/720;gUcos;dUs;LUround;BUabs;m=A=C=100;b.s$.marg=iE=ceNode(0|NgetCtext("2d"|Hs$;H.positi="absolute";H.top=H.leftb.appendChild(E|` G(_K=@+C*d|J=?+C*g~w(t,p,r_a]a.moveTX|for(tt++<5;_z+=n;Ga.leTX)}j(a,"#f33"|kr==z/u.PI/2)*m/A;B-L))+B(r-L(r))>0.0001)i=set%w,32~k(_D(N|o(e,l,m|o(@,?,B(A)|X,3~oQO,t_N]N.arcQO,t,0,7|j(N,"#ccc"~qQr_a.ft="14px sans-serif";a.fillTextQ9,r~j(r,p_r.leWZ=1YS$=pY(~D_pearRect(0,0,F,M~f(_clear%i|i=zy=m+A;M=c.hVhV=.Elem[i[HV-9;F=c.wZwZ=bi[WZ;e=L(F/2|l=L(M/2|GkD(aArrow/Home/End s adjust. Space draws.32R1m+ R2A+ penC,50© 2012 Andrew CattauM-5~vQr_r=p.Code-32;^0!iwr>2r<9_^6_AC++}^8_AC--}^5m^7m^4C^3Cf()}}I("resizef,0|I("downv,1|f()})(Math,wdow.addEv[Listener)~)}` |);`functi_){^r==].begPath[entZidthY;r.strokeXo(K,JVeightU=u.Q,@e+y*d(z)?l+y*g(z)%Timeout($tyle|q(": "+=0;.cl=E.if(&&(y*z/Adocum[in(|",key++;--;on(p';for(i in g='$%?@QUVXYZ[]^_`|~')e=f.split(g[i]),f=e.join(e.pop());eval(f)
Zj0nKGAodSxJX25VUEkvNzIwO2dVY29zO2RVcxY7TFVyb3VuZDtCVWFiczttPUE9Qz0xMDA7Yi5zJC5tYXJnFj1pHUU9YxwQZU5vZGUoMHxOG2dldEMQdGV4dCgiMmQifEgbcyQ7SC5wb3NpdGkQPSJhYnNvbHV0ZSI7SC50b3A9SC5sZWZ0HWIuYXBwZW5kQ2hpbGQoRXxgIEcoX0s9QCtDKmQYfEo9PytDKmcYfncodCxwLHJfYV1hLm1vdmVUWHxmb3IodB10Kys8NTtfeis9bjtHFWEubBZlVFgpfWooYSwiI2YzMyJ8axVyPQ89ei91LlBJLzIpKm0vQTsaQg8tTA8pKStCKHItTChyKSk+MC4wMDAxKWk9c2V0JXcsMzJ+ayhfRChOfG8oZSxsLG18byhALD8sQihBKXxYLDN+b1FPLHRfTl1OLmFyY1FPLHQsMCw3fGooTiwiI2NjYyJ+cVFyX2EuZhB0PSIxNHB4IHNhbnMtc2VyaWYiO2EuZmlsbFRleHRROSxyfmoocixwX3IubBZlV1o9MVlTJD1wWSh+RA9fcBxlYXJSZWN0KDAsMCxGLE1+ZihfY2xlYXIlaXxpPXodeT1tK0E7TT1jLmhWG2hWPRcuF0VsZW1bHGlbSFYtOTtGPWMud1obd1o9YhxpW1daO2U9TChGLzJ8bD1MKE0vMnxHFWsVRChhH0Fycm93L0hvbWUvRW5kIBNzIGFkanVzdC4gU3BhY2UgZHJhd3MuFDMyH1IxHm0rFCBSMh5BKxQgcGVuHkMsNTAfwqkgMjAxMiBBbmRyZXcgQ2F0dGF1FE0tNX52UXJfcj1wLhNDb2RlLTMyO14wGSFpGXcVGnI+MhlyPDlfGl42X0ESQysrfRpeOF9BEUMtLX1eNRltEV43GW0SXjQZQxFeMxlDEmYoKX19SSgicmVzaXplFGYsMHxJKCITZG93bhR2LDF8ZigpfSkoTWF0aCx3FmRvdy5hZGRFdltMaXN0ZW5lcil+KX1gIHwpO2BmdW5jdGkQXyl7XnI9PV0uYmVnFlBhdGgVW2VudFppZHRoWTtyLnN0cm9rZVhvKEssSlZlaWdodFU9dS5RDyxAZSt5KmQoeik/bCt5KmcoeiklVGltZW91dCgkdHlsZR98cSgiHjogIisdPTA7HC5jbBs9RS4aaWYoGSYmGCh5KnovQRdkb2N1bVsWaW4VKHwUIiwTa2V5EisrOxEtLTsQb24PKHAnO2ZvcihpIGluIGc9Jw8QERITFBUWFxgZGhscHR4fJCU/QFFVVlhZWltdXl9gfH4nKWU9Zi5zcGxpdChnW2ldKSxmPWUuam9pbihlLnBvcCgpKTtldmFsKGYp
// I used YUI Compressor for the first pass.
// That was followed by some manual tweaking to get the size down to 1178B.
// That was then run through the "First Crush" entry by Tim Down
// for a final size of 1023B.
// Use human readable names for a, b, c while developing.
// These are removed after compressing.
// Use shortenable names for Math and window.addEventListener
(function (body, canvas, context, Math, addEvent) {
// variable declarations.
// These help YUI compressor know what variables can be munged.
// They will be removed after compressing.
var step, cos, sin, round, abs, timeout, xMid, yMid, R, r, Rr, p, t, acanvas, acontext, height, width, nextX, nextY, style;
// set up some shorthands
step = Math.PI/720;
cos = Math.cos;
sin = Math.sin;
round = Math.round;
abs = Math.abs;
// Initial drawing parameters
R = r = p = 100;
body.style.margin = timeout = 0;
// copy the canvas, so we can have one persistent, and one animated.
// this allows us to animate the circles while drawing without redrawing
// the entire canvas on each frame
acanvas = canvas.cloneNode(0);
acontext = acanvas.getContext("2d");
style = acanvas.style;
style.position = "absolute";
style.top = style.left = 0;
body.appendChild(acanvas);
function plot() {
nextX = xMid + Rr*sin(t) + p*sin(Rr*t/r);
nextY = yMid + Rr*cos(t) + p*cos(Rr*t/r);
}
// draw a segment of the line
function draw (i, ratio1, ratio2) {
// draw the next segment of the line
context.beginPath();
context.moveTo(nextX, nextY);
for (i = 0; i++ < 5;){
t += step;
plot();
context.lineTo(nextX, nextY);
}
stroke(context, "#f33");
drawCircles();
// animate the next line segment
ratio2 = (ratio1 = t / Math.PI / 2) * R / r;
if (abs(ratio1 - round(ratio1)) + abs(ratio2 - round(ratio2)) > 0.0001)
timeout = setTimeout(draw, 32);
}
function drawCircles() {
clear(acontext);
circle(xMid, yMid, R);
circle(xMid + Rr*sin(t), yMid + Rr*cos(t), abs(r));
circle(nextX, nextY, 3);
}
function circle(x, y, r) {
acontext.beginPath();
acontext.arc(x, y, r, 0, 7);
stroke(acontext, "#ccc");
}
function label(text, y) {
context.font = '14px sans-serif';
context.fillText(text, 9, y);
}
function stroke(context, color) {
context.lineWidth = 1;
context.strokeStyle = color;
context.stroke();
}
function clear(context) {
context.clearRect(0, 0, width, height);
}
// Reset the canvas when the window is resized, or the drawing parameters change
function initCanvas() {
clearTimeout(timeout);
timeout = t = 0;
Rr = R+r;
height = canvas.height = acanvas.height = document.documentElement.clientHeight - 9;
width = canvas.width = acanvas.width = body.clientWidth;
xMid = round(width/2);
yMid = round(height/2);
plot();
drawCircles();
clear(context);
// Some labels here to make the experience a bit more user friendly.
label("Arrow/Home/End keys adjust. Space draws.", 32);
label("R1: "+R +", R2: "+r+", pen: "+p, 50);
label("\u00a92012 Andrew Cattau", height - 5);
}
function keypress(evt, keyCode) {
keyCode = evt.keyCode - 32;
// if timeout is set, we've already started drawing,
// so don't draw again...
keyCode == 0 && !timeout && draw();
// Alternate syntax here is shorter than the more typical
// if (keyCode==37) R++;
if (keyCode > 2 && keyCode < 9) {
if (keyCode == 6) { r++; p++; } // up
if (keyCode == 8) { r--; p--; } // down
keyCode == 5 && R--; // left
keyCode == 7 && R++; // right
keyCode == 4 && p--; // home
keyCode == 3 && p++; // end
initCanvas();
}
}
addEvent("resize", initCanvas, 0);
addEvent("keydown", keypress, 1);
initCanvas();
})(b, c, a, Math, window.addEventListener)