Parallax scrolling of multiple repeating background layers composited on a single canvas : Starfield, Fractal mountains, and IFS trees.
W=800;S=2*W;R=H=600;M=[];c.width=W;c.height=H;function r(){R=997*R>>>0;return R/(-1>>>0)}function m(a,b,e,f,d){1<e-a?(x=a+e>>1,y=(b+f+(4*r()-2)*H/6/(1<<d))/2,m(a,b,x,y,d+1),m(x,y,e,f,d+1)):M[a]=b}function n(a,b,e,f,d){if(d){C.lineWidth=d;C.ba();C.m(a,b);a+=e*Math.cos(f);b+=e*Math.sin(f);C.strokeStyle="#"+190*d;C.l(a,b);C.s();for(var g=0;7>g;g++)n(a,b,.7*e,f+2*r()-1,d-1);C.ca()}}t="";for(z=0;7>z;z++){B=c.cloneNode();B.width=S;for(p in C=B.getContext("2d"))C[p[0]+(p[6]||"")]=C[p];if(z)if(5>z){j=H/6*(5-z);m(0,j,S,j,-2);G=C.cL(0,0,0,H);for(i=1;3>i;i++)G.addColorStop(i-1,"hsl("+j/5+",100%,"+3*i+"0%)");C.fillStyle=G;for(i=0;i<S;i++)C.fc(i,M[i],1,H)}else{C.fillStyle=5<z?"#CCC":"#FFF";for(i=0;i<S;i++)C.fc(r()*S,r()*H,7-z,7-z);5<z&&(C.font="160px Arial",C.fx("\u25cf",W,H/4))}else for(j=1;4>j;j++)n(420*j,H,60*j,-1.57,5);t+="url("+B.toDataURL()+"),"}c.style.background=t+"#000";setInterval(function(){t="";for(z=0;7>z;z++)M[z]+=z-7,t+=M[z]+"px 0,";c.style.backgroundPosition=t+0},50/3);
Vz04MDA7Uz0yKlc7Uj1IPTYwMDtNPVtdO2Mud2lkdGg9VztjLmhlaWdodD1IO2Z1bmN0aW9uIHIoKXtSPTk5NypSPj4+MDtyZXR1cm4gUi8oLTE+Pj4wKX1mdW5jdGlvbiBtKGEsYixlLGYsZCl7MTxlLWE/KHg9YStlPj4xLHk9KGIrZisoNCpyKCktMikqSC82LygxPDxkKSkvMixtKGEsYix4LHksZCsxKSxtKHgseSxlLGYsZCsxKSk6TVthXT1ifWZ1bmN0aW9uIG4oYSxiLGUsZixkKXtpZihkKXtDLmxpbmVXaWR0aD1kO0MuYmEoKTtDLm0oYSxiKTthKz1lKk1hdGguY29zKGYpO2IrPWUqTWF0aC5zaW4oZik7Qy5zdHJva2VTdHlsZT0iIyIrMTkwKmQ7Qy5sKGEsYik7Qy5zKCk7Zm9yKHZhciBnPTA7Nz5nO2crKyluKGEsYiwuNyplLGYrMipyKCktMSxkLTEpO0MuY2EoKX19dD0iIjtmb3Ioej0wOzc+ejt6Kyspe0I9Yy5jbG9uZU5vZGUoKTtCLndpZHRoPVM7Zm9yKHAgaW4gQz1CLmdldENvbnRleHQoIjJkIikpQ1twWzBdKyhwWzZdfHwiIildPUNbcF07aWYoeilpZig1Pnope2o9SC82Kig1LXopO20oMCxqLFMsaiwtMik7Rz1DLmNMKDAsMCwwLEgpO2ZvcihpPTE7Mz5pO2krKylHLmFkZENvbG9yU3RvcChpLTEsImhzbCgiK2ovNSsiLDEwMCUsIiszKmkrIjAlKSIpO0MuZmlsbFN0eWxlPUc7Zm9yKGk9MDtpPFM7aSsrKUMuZmMoaSxNW2ldLDEsSCl9ZWxzZXtDLmZpbGxTdHlsZT01PHo/IiNDQ0MiOiIjRkZGIjtmb3IoaT0wO2k8UztpKyspQy5mYyhyKCkqUyxyKCkqSCw3LXosNy16KTs1PHomJihDLmZvbnQ9IjE2MHB4IEFyaWFsIixDLmZ4KCJcdTI1Y2YiLFcsSC80KSl9ZWxzZSBmb3Ioaj0xOzQ+ajtqKyspbig0MjAqaixILDYwKmosLTEuNTcsNSk7dCs9InVybCgiK0IudG9EYXRhVVJMKCkrIiksIn1jLnN0eWxlLmJhY2tncm91bmQ9dCsiIzAwMCI7c2V0SW50ZXJ2YWwoZnVuY3Rpb24oKXt0PSIiO2Zvcih6PTA7Nz56O3orKylNW3pdKz16LTcsdCs9TVt6XSsicHggMCwiO2Muc3R5bGUuYmFja2dyb3VuZFBvc2l0aW9uPXQrMH0sNTAvMyk7
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +--------------------------------------------------------------+
// | JS1K PARALLAX SCROLLING |
// +--------------------------------------------------------------+
// | |
// | Multiple repeating background layers composited on a single |
// | canvas : Starfield, Fractal mountains, IFS trees. |
// | |
// | Uses the mechanized abbreviation of the canvas context |
// | methods from Marijn Haverbeke, and a custon seedable pseudo |
// | random number generator. |
// | |
// | For smart animating, requestAnimationFrame would be better |
// | than setInterval, but the shortest polyfill i've managed to |
// | design is 50 bytes long and does not work with Opera (the |
// | vendor-prefix method is not yet implemented, and it requires |
// | a setTimeout fallback). FYI, here it is : |
// | for (p in w = window) |
// | /^.{1,6}R.*nF/.test(p) && w[p](animate_function); |
// | |
// +--------------------------------------------------------------+
// | Copyright(C) 2013 Frederic Poeydomenge fpoeydomenge@free.fr |
// +--------------------------------------------------------------+
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//=================
// Initializations
//=================
W = 800; // Screen width
S = W * 2; // Double screen width
R = // Current random number
H = 600; // Screen height
M = []; // Computed values
c.width = W; // Canvas width
c.height = H; // Canvas height
//=======================================
// PRNG - Pseudo Random Number Generator
//=======================================
function r() {
R = 997 * R >>> 0;
return R / 4294967295;
}
//================================
// Mountains - Recursive function
//================================
function m(x1, h1, x2, h2, deep) {
x2 - x1 > 1
? (
x = x1 + x2 >> 1,
y = (h1 + h2 + (4 * r() - 2) * H / 6 / (1 << deep)) / 2,
m(x1, h1, x, y, deep + 1),
m(x, y, x2, h2, deep + 1)
)
: M[x1] = h1;
}
//===============================
// IFS Tree - Recursive function
//===============================
function n(start_x, start_y, length, angle, size) {
if (size) {
C.lineWidth = size;
// C.beginPath
C.ba();
// C.moveTo
C.m(start_x, start_y);
start_x += length * Math.cos(angle);
start_y += length * Math.sin(angle);
C.strokeStyle = '#' + size * 190;
// C.lineTo
C.l(start_x, start_y);
// C.stroke
C.s();
for (var i = 0 ; i < 7 ; i++)
n(start_x, start_y, length * .7, angle + r() * 2 - 1, size - 1);
// C.closePath
C.ca();
}
}
//================
// Creates layers
//================
t = '';
for (z = 0 ; z < 7 ; z++) {
// Creates a new canvas element (2 screens wide)
B = c.cloneNode(); // B=document.createElement('canvas');
B.width = S; // B.height is inherited from previously defined c element
// Mechanized abbreviations
// http://marijnhaverbeke.nl/js1k/
for (p in C = B.getContext('2d'))
C[p[0] + (p[6] || '')] = C[p];
// Layer 0 : IFS Tree
if (!z)
for (j = 1 ; j < 4 ; j++)
n(420 * j, H, 60 * j, -1.57, 5);
// Layers 1 to 4 : Mountains
else if (z < 5) {
j = H / 6 * (5 - z);
m(0, j, S, j, -2);
// C.createLinearGradient
G = C.cL(0, 0, 0, H);
for (i = 1 ; i < 3 ; i++)
G.addColorStop(i - 1, 'hsl(' + j / 5 + ',100%,' + 3 * i + '0%)');
C.fillStyle = G;
for (i = 0 ; i < S ; i++)
// C.fillRect
C.fc(i, M[i], 1, H);
}
// Layers 5 to 6 : Starfield + Moon
else {
C.fillStyle = z > 5 ? '#CCC' : '#FFF';
for (i = 0 ; i < S ; i++)
// C.fillRect
C.fc(r() * S, r() * H, 7 - z, 7 - z);
// Layers 6 : Moon
if (z > 5)
C.font = '160px Arial',
// C.fillText
C.fx('\u25cf', W, H / 4);
}
// CSS3 multiple background property
t += 'url(' + B.toDataURL() + '),';
}
//========================
// Initializes the canvas
//========================
c.style.background = t + '#000';
//======================
// Starts the animation
//======================
setInterval(
function() {
t = '';
for (z = 0 ; z < 7 ; z++)
M[z] += z - 7,
t += M[z] + 'px 0,';
c.style.backgroundPosition = t + 0;
},
50 / 3
);