UNPKG

lava-lamp-bubbles

Version:
254 lines (253 loc) 9.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var LavaLampBubbles = /** @class */ (function () { function LavaLampBubbles(canvasID, speed, color1, color2) { var _this = this; // canvas this.screen = new Ge1doot(canvasID).init(true); this.ctx = this.screen.ctx; this.screen.resize(); window.addEventListener("resize", function () { return _this.screen.resize(); }); // create LavaLamps this.lava0 = new LavaLamp(this.ctx, this.screen.width, this.screen.height, speed, 10, color1, color2); } LavaLampBubbles.prototype.start = function () { var _this = this; requestAnimationFrame(function () { return _this.start(); }); this.ctx.clearRect(0, 0, screen.width, screen.height); this.lava0.renderMetaballs(); }; return LavaLampBubbles; }()); exports.default = LavaLampBubbles; var Ge1doot = /** @class */ (function () { function Ge1doot(id) { this.width = 0; this.height = 0; this.left = 0; this.top = 0; this.elem = document.getElementById(id); if (this.elem === null) throw "Invalid Canvas id!"; this.ctx = this.elem.getContext("2d"); } Ge1doot.prototype.init = function (initRes) { var _this = this; window.addEventListener("resize", function () { return _this.resize(); }); this.elem.onselectstart = function () { return false; }; this.elem.ondrag = function () { return false; }; initRes && this.resize(); return this; }; Ge1doot.prototype.resize = function () { var o = this.elem; this.width = o.offsetWidth; this.height = o.offsetHeight; for (this.left = 0, this.top = 0; o != null; o = o.offsetParent) { this.left += o.offsetLeft; this.top += o.offsetTop; } if (this.ctx) { this.elem.width = this.width; this.elem.height = this.height; } }; return Ge1doot; }()); var Point = /** @class */ (function () { function Point(x, y) { this.computed = 0; this.force = 0; this.x = x; this.y = y; this.magnitude = x * x + y * y; this.computed = 0; this.force = 0; } Point.prototype.add = function (p) { return new Point(this.x + p.x, this.y + p.y); }; return Point; }()); var Ball = /** @class */ (function () { function Ball(par) { var min = 0.05; var max = 0.075; this.vel = new Point((Math.random() > 0.5 ? 1 : -1) * (0.2 + Math.random() * 0.25) * 0.25 * par.speed, (Math.random() > 0.5 ? 1 : -1) * (0.2 + Math.random()) * 0.25 * par.speed); this.pos = new Point(par.width * 0.2 + Math.random() * par.width * 0.6, par.height * 0.2 + Math.random() * par.height * 0.6); this.size = par.wh / 15 + (Math.random() * (max - min) + min) * (par.wh / 15); this.width = par.width; this.height = par.height; } Ball.prototype.move = function () { // bounce borders if (this.pos.x >= this.width - this.size) { if (this.vel.x > 0) this.vel.x = -this.vel.x; this.pos.x = this.width - this.size; } else if (this.pos.x <= this.size) { if (this.vel.x < 0) this.vel.x = -this.vel.x; this.pos.x = this.size; } if (this.pos.y >= this.height - this.size) { if (this.vel.y > 0) this.vel.y = -this.vel.y; this.pos.y = this.height - this.size; } else if (this.pos.y <= this.size) { if (this.vel.y < 0) this.vel.y = -this.vel.y; this.pos.y = this.size; } // velocity this.pos = this.pos.add(this.vel); }; return Ball; }()); var LavaLamp = /** @class */ (function () { function LavaLamp(ctx, width, height, speed, numBalls, c0, c1) { this.step = 5; this.paint = false; this.iter = 0; this.sign = 1; this.ctx = ctx; this.step = 5; this.width = width; this.height = height; this.wh = Math.min(width, height); this.sx = Math.floor(this.width / this.step); this.sy = Math.floor(this.height / this.step); this.speed = speed; this.paint = false; this.metaFill = createRadialGradient(this.ctx, width, height, width, c0, c1); this.plx = [0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0]; this.ply = [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1]; this.mscases = [0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 0, 2, 1, 1, 0]; this.ix = [1, 0, -1, 0, 0, 1, 0, -1, -1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1]; this.grid = []; this.balls = []; this.iter = 0; this.sign = 1; // init grid for (var i = 0; i < (this.sx + 2) * (this.sy + 2); i++) { this.grid[i] = new Point((i % (this.sx + 2)) * this.step, Math.floor(i / (this.sx + 2)) * this.step); } // create metaballs for (var k = 0; k < numBalls; k++) { this.balls[k] = new Ball(this); } } LavaLamp.prototype.computeForce = function (x, y, idx) { var force; var id = idx || x + y * (this.sx + 2); if (x === 0 || y === 0 || x === this.sx || y === this.sy) { force = 0.6 * this.sign; } else { force = 0; var cell = this.grid[id]; var i = 0; var ball = void 0; while ((ball = this.balls[i++])) { force += (ball.size * ball.size) / (-2 * cell.x * ball.pos.x - 2 * cell.y * ball.pos.y + ball.pos.magnitude + cell.magnitude); } force *= this.sign; } this.grid[id].force = force; return force; }; LavaLamp.prototype.marchingSquares = function (next) { var x = next[0]; var y = next[1]; var pdir = next[2]; var id = x + y * (this.sx + 2); if (this.grid[id].computed === this.iter) { return false; } var dir; var mscase = 0; // neighbors force for (var i = 0; i < 4; i++) { var idn = x + this.ix[i + 12] + (y + this.ix[i + 16]) * (this.sx + 2); var force = this.grid[idn].force; if ((force > 0 && this.sign < 0) || (force < 0 && this.sign > 0) || !force) { // compute force if not in buffer force = this.computeForce(x + this.ix[i + 12], y + this.ix[i + 16], idn); } if (Math.abs(force) > 1) mscase += Math.pow(2, i); } if (mscase === 15) { // inside return [x, y - 1, false]; } else { // ambiguous cases if (mscase === 5) dir = pdir === 2 ? 3 : 1; else if (mscase === 10) dir = pdir === 3 ? 0 : 2; else { // lookup dir = this.mscases[mscase]; this.grid[id].computed = this.iter; } // draw line var ix = this.step / (Math.abs(Math.abs(this.grid[x + this.plx[4 * dir + 2] + (y + this.ply[4 * dir + 2]) * (this.sx + 2)].force) - 1) / Math.abs(Math.abs(this.grid[x + this.plx[4 * dir + 3] + (y + this.ply[4 * dir + 3]) * (this.sx + 2)].force) - 1) + 1); this.ctx.lineTo(this.grid[x + this.plx[4 * dir] + (y + this.ply[4 * dir]) * (this.sx + 2)].x + this.ix[dir] * ix, this.grid[x + this.plx[4 * dir + 1] + (y + this.ply[4 * dir + 1]) * (this.sx + 2)].y + this.ix[dir + 4] * ix); this.paint = true; // next return [x + this.ix[dir + 4], y + this.ix[dir + 8], dir]; } }; LavaLamp.prototype.renderMetaballs = function () { var i = 0; var ball; while ((ball = this.balls[i++])) ball.move(); // reset grid this.iter++; this.sign = -this.sign; this.paint = false; this.ctx.fillStyle = this.metaFill; this.ctx.beginPath(); // compute metaballs i = 0; while ((ball = this.balls[i++])) { // first cell var next = [Math.round(ball.pos.x / this.step), Math.round(ball.pos.y / this.step), false]; // marching squares while (next) { next = this.marchingSquares(next); } // fill and close path if (this.paint) { this.ctx.fill(); this.ctx.closePath(); this.ctx.beginPath(); this.paint = false; } } }; return LavaLamp; }()); // gradients function createRadialGradient(ctx, w, h, r, c0, c1) { var gradient = ctx.createRadialGradient(w / 1, h / 1, 0, w / 1, h / 1, r); gradient.addColorStop(0, c0); gradient.addColorStop(1, c1); return gradient; }