JavaScript
语言:
JaveScriptBabelCoffeeScript
确定
"use strict";
const transform = {
m: [],
minSize: 0.1,
stack: [],
calls: [],
shapes: [],
reset() {
this.m = [1, 0, 0, 1, 0, 0, 0, 0];
this.shapes.length = 0;
this.calls.length = 0;
return this;
},
rotate(v) {
const m = this.m;
const rad = Math.PI * v / 180;
const cos = Math.cos(rad);
const sin = Math.sin(rad);
const r00 = cos * m[0] + sin * m[2];
const r01 = cos * m[1] + sin * m[3];
m[2] = cos * m[2] - sin * m[0];
m[3] = cos * m[3] - sin * m[1];
m[0] = r00;
m[1] = r01;
return this;
},
translate(x, y = 0) {
const m = this.m;
m[4] += x * m[0] + y * m[2];
m[5] += x * m[1] + y * m[3];
return this;
},
scale(x = 1, y = x) {
const m = this.m;
m[0] *= x;
m[1] *= x;
m[2] *= y;
m[3] *= y;
return this;
},
z(v) {
this.m[6] += v;
return this;
},
light(v) {
this.m[7] = v;
return this;
},
exec(fn) {
this.calls.push([fn, this.m.slice()]);
return this;
},
save() {
this.stack.push(this.m.slice());
return this;
},
restore() {
this.m = this.stack.pop();
return this;
},
SQUARE() {
this.shapes.push(this.m.slice());
return this;
},
tooSmall() {
const m = this.m;
const x = m[0] * m[0] + m[1] * m[1];
const y = m[2] * m[2] + m[3] * m[3];
return x < this.minSize || y < this.minSize;
},
render(ctx, w, h) {
do {
const step = this.calls.shift();
this.m = step[1];
step[0]();
} while (this.calls.length);
this.shapes.sort(function(a, b) {
return a[6] - b[6];
});
for (const m of this.shapes) {
ctx.setTransform(
m[0],
m[1],
m[2],
m[3],
m[4] + w * 0.5,
m[5] + h * 0.5
);
ctx.fillStyle = `hsl(0, 0%, ${m[7]}%)`;
ctx.fillRect(-0.5, -0.5, 1, 1);
}
}
};
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const walk = () => {
const r = Math.random() * 5;
switch (true) {
case r <= 1:
transform.translate(0, 0.2);
transform.exec(walk);
break;
case r <= 2:
transform.translate(0.5, 0);
transform.exec(walk);
break;
case r <= 3:
transform.translate(-0.5, 0);
transform.exec(walk);
break;
default:
transform
.save()
.translate(r <= 4 ? -0.2 : 0.2, 1)
.scale(0.9)
.z(-0.1);
if (transform.tooSmall()) return;
transform
.exec(walk)
.restore();
transform
.translate(0, 1)
.exec(sides);
}
};
const sides = () => {
transform
.save()
.rotate([120, 130, 110][(Math.random() * 3) | 0])
.exec(leftside)
.restore();
transform
.save()
.rotate([-120, -130, -110][(Math.random() * 3) | 0])
.exec(rightside)
.restore();
};
const leftside = () => {
transform
.save()
.translate(-0.5, 0.5)
.light(100)
.SQUARE()
.restore();
transform
.save()
.translate(0, 0.5)
.scale(0.1, 1)
.SQUARE()
.restore();
const r = Math.random() * 2.1;
if (r <= 0.1) return;
transform
.translate(0, 1)
.rotate(r <= 1.1 ? 5 : -5)
.exec(leftside);
};
const rightside = () => {
transform
.scale(-1, 1)
.exec(leftside);
};
const start = () => {
const width = (canvas.width = canvas.offsetWidth * 4);
const height = (canvas.height = canvas.offsetHeight * 4);
ctx.clearRect(0, 0, width, height);
const scale = Math.min(width, height) / 100;
transform.reset().scale(scale * 4, -scale * 4).translate(-10, -5);
for (let i = 0; i < 10; i++) {
transform.translate(2, 0).z(-0.01);
transform.exec(walk);
}
transform.render(ctx, width, height);
};
window.addEventListener("resize", start, false);
["click", "touchdown"].forEach(event => {
document.addEventListener(event, start, false);
});
requestAnimationFrame(start);