Found code here for metaballs using vanilla javascript. Is there a way I can change the opacity of the metaballs? I think it should be a quick fix but im not too sure. I tried changing to CSS class bubbles but that didnt work so im assuming in needs to be in the Javascript. Anything helps, thanks.
Found code here for metaballs using vanilla javascript. Is there a way I can change the opacity of the metaballs? I think it should be a quick fix but im not too sure. I tried changing to CSS class bubbles but that didnt work so im assuming in needs to be in the Javascript. Anything helps, thanks.
;(function() {
"use strict";
var lava0;
var ge1doot = {
screen: {
elem: null,
callback: null,
ctx: null,
width: 0,
height: 0,
left: 0,
top: 0,
init: function (id, callback, initRes) {
this.elem = document.getElementById(id);
this.callback = callback || null;
if (this.elem.tagName == "CANVAS") this.ctx = this.elem.getContext("2d");
window.addEventListener('resize', function () {
this.resize();
}.bind(this), false);
this.elem.onselectstart = function () { return false; }
this.elem.ondrag = function () { return false; }
initRes && this.resize();
return this;
},
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;
}
this.callback && this.callback();
}
}
}
// Point constructor
var Point = function(x, y) {
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);
};
// Ball constructor
var Ball = function(parent) {
var min = .1;
var max = 1.5;
this.vel = new Point(
(Math.random() > 0.5 ? 1 : -1) * (0.2 + Math.random() * 0.25), (Math.random() > 0.5 ? 1 : -1) * (0.2 + Math.random())
);
this.pos = new Point(
parent.width * 0.2 + Math.random() * parent.width * 0.6,
parent.height * 0.2 + Math.random() * parent.height * 0.6
);
this.size = (parent.wh / 15) + ( Math.random() * (max - min) + min ) * (parent.wh / 15);
this.width = parent.width;
this.height = parent.height;
};
// move balls
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);
};
// lavalamp constructor
var LavaLamp = function(width, height, numBalls, c0, c1) {
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.paint = false;
this.metaFill = createRadialGradient(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);
}
};
// compute cell force
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;
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;
};
// compute cell
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, 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
);
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, ball;
while (ball = this.balls[i++]) ball.move();
// reset grid
this.iter++;
this.sign = -this.sign;
this.paint = false;
ctx.fillStyle = this.metaFill;
ctx.beginPath();
// compute metaballs
i = 0;
//ctx.shadowBlur = 50;
//ctx.shadowColor = "green";
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
do {
next = this.marchingSquares(next);
} while (next);
// fill and close path
if (this.paint) {
ctx.fill();
ctx.closePath();
ctx.beginPath();
this.paint = false;
}
}
};
// gradients
var createRadialGradient = function(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);
};
// main loop
var run = function() {
requestAnimationFrame(run);
ctx.clearRect(0, 0, screen.width, screen.height);
lava0.renderMetaballs();
};
// canvas
var screen = ge1doot.screen.init("bubble", null, true),
ctx = screen.ctx;
screen.resize();
// create LavaLamps
lava0 = new LavaLamp(screen.width, screen.height, 6, "#FF9298", "#E4008E");
run();
})();
body {
margin: 0;
}
.wrap {
overflow: hidden;
position: relative;
height: 100vh;
}
canvas {
width: 100%;
height: 100%;
}
<div class="wrap">
<canvas id="bubble"></canvas>
</div>
You can do it by changing the context alpha channel with RGBA (see at the very bottom ctx.fillStyle = 'rgba(0, 0, 255, 0.5)' // NEW! where 0.5 is the level of opacity - see ) :
;
(function() {
'use strict'
var lava0
var ge1doot = {
screen: {
elem: null,
callback: null,
ctx: null,
width: 0,
height: 0,
left: 0,
top: 0,
init: function(id, callback, initRes) {
this.elem = document.getElementById(id)
this.callback = callback || null
if (this.elem.tagName == 'CANVAS') this.ctx = this.elem.getContext('2d')
window.addEventListener(
'resize',
function() {
this.resize()
}.bind(this),
false
)
this.elem.onselectstart = function() {
return false
}
this.elem.ondrag = function() {
return false
}
initRes && this.resize()
return this
},
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
}
this.callback && this.callback()
},
},
}
// Point constructor
var Point = function(x, y) {
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)
}
// Ball constructor
var Ball = function(parent) {
var min = 0.1
var max = 1.5
this.vel = new Point(
(Math.random() > 0.5 ? 1 : -1) * (0.2 + Math.random() * 0.25),
(Math.random() > 0.5 ? 1 : -1) * (0.2 + Math.random())
)
this.pos = new Point(
parent.width * 0.2 + Math.random() * parent.width * 0.6,
parent.height * 0.2 + Math.random() * parent.height * 0.6
)
this.size = parent.wh / 15 + (Math.random() * (max - min) + min) * (parent.wh / 15)
this.width = parent.width
this.height = parent.height
}
// move balls
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)
}
// lavalamp constructor
var LavaLamp = function(width, height, numBalls, c0, c1) {
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.paint = false
this.metaFill = createRadialGradient(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)
}
}
// compute cell force
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
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
}
// compute cell
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,
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)
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,
ball
while ((ball = this.balls[i++])) ball.move()
// reset grid
this.iter++
this.sign = -this.sign
this.paint = false
ctx.fillStyle = this.metaFill
ctx.beginPath()
// compute metaballs
i = 0
//ctx.shadowBlur = 50;
//ctx.shadowColor = "green";
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
do {
next = this.marchingSquares(next)
} while (next)
// fill and close path
if (this.paint) {
ctx.fill()
ctx.closePath()
ctx.beginPath()
this.paint = false
}
}
}
// gradients
var createRadialGradient = function(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)
}
// main loop
var run = function() {
requestAnimationFrame(run)
ctx.clearRect(0, 0, screen.width, screen.height)
ctx.fillStyle = 'rgba(0, 0, 255, 0.5)' // NEW!
lava0.renderMetaballs()
}
// canvas
var screen = ge1doot.screen.init('bubble', null, true),
ctx = screen.ctx
screen.resize()
// create LavaLamps
lava0 = new LavaLamp(screen.width, screen.height, 6, '#FF9298', '#E4008E')
run()
})()
body {
margin: 0;
}
.wrap {
overflow: hidden;
position: relative;
height: 100vh;
}
canvas {
width: 100%;
height: 100%;
}
<div class="wrap">
<canvas id="bubble"></canvas>
</div>
Your Lava constructor takes two colors, these can be modified to fit your color needs. By using rgba() versions of your colors, you can set the alpha (ie the opacity) or your bubbles/meatballs. However, before you do that, you need to return the gradient created in createRadialGradient so that the colors can be used:
var createRadialGradient = function(w, h, r, c0, c1) {
// ... code ...
return gradient; // add this line
};
Now you can modify how you call your constructor:
// rgba versions of your colors -----------------------\/
lava0 = new LavaLamp(screen.width, screen.height, 6, "rgba(255, 146, 152, 0.5)", "rgba(228, 0, 142, 0.5)");
;(function() {
"use strict";
var lava0;
var ge1doot = {
screen: {
elem: null,
callback: null,
ctx: null,
width: 0,
height: 0,
left: 0,
top: 0,
init: function (id, callback, initRes) {
this.elem = document.getElementById(id);
this.callback = callback || null;
if (this.elem.tagName == "CANVAS") this.ctx = this.elem.getContext("2d");
window.addEventListener('resize', function () {
this.resize();
}.bind(this), false);
this.elem.onselectstart = function () { return false; }
this.elem.ondrag = function () { return false; }
initRes && this.resize();
return this;
},
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;
}
this.callback && this.callback();
}
}
}
// Point constructor
var Point = function(x, y) {
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);
};
// Ball constructor
var Ball = function(parent) {
var min = .1;
var max = 1.5;
this.vel = new Point(
(Math.random() > 0.5 ? 1 : -1) * (0.2 + Math.random() * 0.25), (Math.random() > 0.5 ? 1 : -1) * (0.2 + Math.random())
);
this.pos = new Point(
parent.width * 0.2 + Math.random() * parent.width * 0.6,
parent.height * 0.2 + Math.random() * parent.height * 0.6
);
this.size = (parent.wh / 15) + ( Math.random() * (max - min) + min ) * (parent.wh / 15);
this.width = parent.width;
this.height = parent.height;
};
// move balls
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);
};
// lavalamp constructor
var LavaLamp = function(width, height, numBalls, c0, c1) {
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.paint = false;
this.metaFill = createRadialGradient(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);
}
};
// compute cell force
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;
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;
};
// compute cell
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, 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
);
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, ball;
while (ball = this.balls[i++]) ball.move();
// reset grid
this.iter++;
this.sign = -this.sign;
this.paint = false;
ctx.fillStyle = this.metaFill;
ctx.beginPath();
// compute metaballs
i = 0;
//ctx.shadowBlur = 50;
//ctx.shadowColor = "green";
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
do {
next = this.marchingSquares(next);
} while (next);
// fill and close path
if (this.paint) {
ctx.fill();
ctx.closePath();
ctx.beginPath();
this.paint = false;
}
}
};
// gradients
var createRadialGradient = function(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;
};
// main loop
var run = function() {
requestAnimationFrame(run);
ctx.clearRect(0, 0, screen.width, screen.height);
lava0.renderMetaballs();
};
// canvas
var screen = ge1doot.screen.init("bubble", null, true),
ctx = screen.ctx;
screen.resize();
// create LavaLamps
lava0 = new LavaLamp(screen.width, screen.height, 6, "rgba(255, 146, 152, 0.5)", "rgba(228, 0, 142, 0.5)");
run();
})();
body {
margin: 0;
}
.wrap {
overflow: hidden;
position: relative;
height: 100vh;
}
canvas {
width: 100%;
height: 100%;
}
<div class="wrap">
<canvas id="bubble"></canvas>
</div>
Related
I basically have 3 curved bezier lines rotating around with a somewhat dynamic background.
It is done in 2D canvas, it works fine in chromium, but in Firefox I get 1 FPS for some reason.
I tried various solutions but while some did give me like 20% performance increase, 1.2 FPS is still unacceptable.
Here is the codepen with the code.
class BG {
animationContainer = null;
options = {
rotation: 45,
waves: 3,
width: 300,
hue: [14, 50],
amplitude: .5,
background: 1,
preload: 1,
speed: [.001, .004]
};
waves = [];
hue = 0;
hueFw = true;
canvas = null;
ctx = null;
observer = null;
radius = 0;
centerX = 0;
centerY = 0;
width = 0;
height = 0;
scale = 1;
isVisible = false;
gradient = null;
color = "#000000";
constructor(t, e) {
this.animationContainer = t, this.options = {
rotation: e.rotation || 45,
waves: e.waves || 2,
width: e.width || 300,
hue: e.hue || [17, 50],
amplitude: e.amplitude || .75,
background: e.background || 1,
preload: e.preload || 1,
speed: e.speed || [.001, .004]
}, this.waves = [], this.hue = this.options.hue[0], this.hueFw = true, this.setup(), this.render()
}
setup() {
this.canvas = document.createElement("canvas"), this.ctx = this.canvas.getContext("2d"), this.animationContainer.appendChild(this.canvas), this.resize(), window.addEventListener("resize", this.resize.bind(this)), this.waves = [];
for (let t = 0; t < this.options.waves; t++) this.waves.push(WV(this));
this.options.preload && this.preload(), this.addIntersection()
}
resize() {
let {
offsetWidth: t,
offsetHeight: e
} = this.animationContainer;
this.scale = window.devicePixelRatio || 1, this.width = t * this.scale, this.height = e * this.scale, this.canvas.width = this.width, this.canvas.height = this.height, this.canvas.style.width = `${t}px`, this.canvas.style.height = `${e}px`, this.radius = Math.sqrt(Math.pow(this.width, 2) + Math.pow(this.height, 2)) / 2, this.centerX = this.width / 2, this.centerY = this.height / 2;
}
addIntersection() {
this.observer = new IntersectionObserver(t => {
for (let e of t) this.isVisible = e.intersectionRatio > 0
}), this.observer.observe(this.animationContainer)
}
preload() {
for (let t = 0; t < this.options.waves; t++) {
this.updateColor();
for (let e = 0; e < this.options.width; e++) this.waves[t].update()
}
}
updateColor() {
this.hue += this.hueFw ? .01 : -.01, this.hue > this.options.hue[1] && this.hueFw ? this.hueFw = false : this.hue < this.options.hue[0] && !this.hueFw && (this.hueFw = true);
let t = Math.floor(127 * Math.sin(.3 * this.hue) + 128),
e = Math.floor(127 * Math.sin(.3 * this.hue + 2) + 128),
i = Math.floor(127 * Math.sin(.3 * this.hue + 4) + 128);
this.color = "rgba(" + t + "," + e + "," + i + ", 0.1)"
}
render() {
requestAnimationFrame(() => {
if (this.isVisible) {
this.draw();
}
this.render();
});
}
draw() {
for (let t of (this.updateColor(), this.clear(), this.options.background && this.background(), this.waves)) t.update(), t.draw()
}
clear() {
this.ctx.clearRect(0, 0, this.width, this.height);
}
background() {
this.gradient = this.ctx.createLinearGradient(0, 0, 0, this.height), this.gradient.addColorStop(1, "#0E1218"), this.gradient.addColorStop(0, this.color), this.ctx.fillStyle = this.gradient, this.ctx.fillRect(1, 0, this.width, this.height)
}
}
function LN(t, e) {
t.angle[0] += t.speed[0];
t.angle[1] += t.speed[1];
t.angle[2] += t.speed[2];
t.angle[3] += t.speed[3];
return { angle: [Math.sin(t.angle[0]), Math.sin(t.angle[1]), Math.sin(t.angle[2]), Math.sin(t.angle[3])], wave: t, color: e };
}
function rng(t, e) {
return null == e ? Math.random() * t : t + Math.random() * (e - t);
}
function dr() {
return Math.random() > .5 ? 1 : -1;
}
function WV(t) {
let e = [];
const angles = [
rng(2 * Math.PI),
rng(2 * Math.PI),
rng(2 * Math.PI),
rng(2 * Math.PI)
];
const speeds = [
rng(t.options.speed[0], t.options.speed[1]) * dr(),
rng(t.options.speed[0], t.options.speed[1]) * dr(),
rng(t.options.speed[0], t.options.speed[1]) * dr(),
rng(t.options.speed[0], t.options.speed[1]) * dr()
];
const h = () => {
e.push(LN({ angle: angles, speed: speeds }, t.color));
if (e.length > t.options.width) {
e.shift();
}
};
const n = () => {
const {
ctx: i,
radius: s,
centerX: h,
centerY: n,
options: o
} = t;
const r = s / 3;
const a = (o.rotation * Math.PI) / 360;
const { amplitude: d } = o;
for (const l of e) {
const { angle: $ } = l;
const wave1 = $[0] * d + a;
const wave2 = $[3] * d + a;
const wave3 = $[1] * d * 2;
const wave4 = $[2] * d * 2;
const u = h - s * Math.cos(wave1);
const p = n - s * Math.sin(wave1);
const w = h - r * Math.cos(wave3);
const _ = n - r * Math.sin(wave3);
const v = h + r * Math.cos(wave4);
const b = n + r * Math.sin(wave4);
const c = h + s * Math.cos(wave2);
const g = n + s * Math.sin(wave2);
i.strokeStyle = l.color;
i.beginPath();
i.moveTo(u, p);
i.bezierCurveTo(w, _, v, b, c, g);
i.stroke();
}
};
return { lines: e, angle: angles, speed: speeds, update: h, draw: n };
}
I am trying to replicate this effect - https://codepen.io/jonathasborges1/pen/YzryRpX in my angular app application.
But I'm having a hard time applying the effect
Someone can help me?
"use strict";
var LeafScene = function (el) {
this.viewport = el;
this.world = document.createElement("div");
this.leaves = [];
this.options = {
numLeaves: 60,
wind: {
magnitude: 1.2,
maxSpeed: 12,
duration: 300,
start: 0,
speed: 0
}
};
this.width = this.viewport.offsetWidth;
this.height = this.viewport.offsetHeight;
// animation helper
this.timer = 0;
this._resetLeaf = function (leaf) {
// place leaf towards the top left
leaf.x = this.width * 2 - Math.random() * this.width * 1.75;
leaf.y = -10;
leaf.z = Math.random() * 200;
if (leaf.x > this.width) {
leaf.x = this.width + 10;
leaf.y = (Math.random() * this.height) / 2;
}
// at the start, the leaf can be anywhere
if (this.timer == 0) {
leaf.y = Math.random() * this.height;
}
// Choose axis of rotation.
// If axis is not X, chose a random static x-rotation for greater variability
leaf.rotation.speed = Math.random() * 10;
var randomAxis = Math.random();
if (randomAxis > 0.5) {
leaf.rotation.axis = "X";
}
else if (randomAxis > 0.25) {
leaf.rotation.axis = "Y";
leaf.rotation.x = Math.random() * 180 + 90;
}
else {
leaf.rotation.axis = "Z";
leaf.rotation.x = Math.random() * 360 - 180;
// looks weird if the rotation is too fast around this axis
leaf.rotation.speed = Math.random() * 3;
}
// random speed
leaf.xSpeedVariation = Math.random() * 1 - 0.2;
leaf.ySpeed = Math.random();
return leaf;
};
this._updateLeaf = function (leaf) {
var leafWindSpeed = this.options.wind.speed(this.timer - this.options.wind.start, leaf.y);
var xSpeed = leafWindSpeed + leaf.xSpeedVariation;
leaf.x -= xSpeed;
leaf.y += leaf.ySpeed;
leaf.rotation.value += leaf.rotation.speed;
var t = "translateX( " +
leaf.x +
"px ) translateY( " +
leaf.y +
"px ) translateZ( " +
leaf.z +
"px ) rotate" +
leaf.rotation.axis +
"( " +
leaf.rotation.value +
"deg )";
if (leaf.rotation.axis !== "X") {
t += " rotateX(" + leaf.rotation.x + "deg)";
}
leaf.el.style.webkitTransform = t;
leaf.el.style.MozTransform = t;
leaf.el.style.oTransform = t;
leaf.el.style.transform = t;
// reset if out of view
if (leaf.x < -10 || leaf.y > this.height + 10) {
this._resetLeaf(leaf);
}
};
this._updateWind = function () {
if (this.timer === 0 ||
this.timer > this.options.wind.start + this.options.wind.duration) {
this.options.wind.magnitude = Math.random() * this.options.wind.maxSpeed;
this.options.wind.duration =
this.options.wind.magnitude * 50 + (Math.random() * 20 - 10);
this.options.wind.start = this.timer;
var screenHeight = this.height;
this.options.wind.speed = function (t, y) {
var a = ((this.magnitude / 2) * (screenHeight - (2 * y) / 3)) / screenHeight;
return (a *
Math.sin(((2 * Math.PI) / this.duration) * t + (3 * Math.PI) / 2) +
a);
};
}
};
};
LeafScene.prototype.init = function () {
for (var i = 0; i < this.options.numLeaves; i++) {
var leaf = {
el: document.createElement("div"),
x: 0,
y: 0,
z: 0,
rotation: {
axis: "X",
value: 0,
speed: 0,
x: 0
},
xSpeedVariation: 0,
ySpeed: 0,
path: {
type: 1,
start: 0
},
image: 1
};
this._resetLeaf(leaf);
this.leaves.push(leaf);
this.world.appendChild(leaf.el);
}
this.world.className = "leaf-scene";
this.viewport.appendChild(this.world);
// reset window height/width on resize
var self = this;
window.onresize = function (event) {
self.width = self.viewport.offsetWidth;
self.height = self.viewport.offsetHeight;
};
};
LeafScene.prototype.render = function () {
this._updateWind();
for (var i = 0; i < this.leaves.length; i++) {
this._updateLeaf(this.leaves[i]);
}
this.timer++;
requestAnimationFrame(this.render.bind(this));
};
// start up leaf scene
var leafContainer = document.querySelector(".falling-leaves"), leaves = new LeafScene(leafContainer);
leaves.init();
leaves.render();
You can create a file like <script> ..that js code.. </script> and save it as script.js and then add it to your index.html or dynamicly load it:
public loadScript(){
return new Promise(resolve => {
const scriptElement = document.createElement('script');
scriptElement.src = '/assets/js/script.js'
scriptElement.onload = resolve;
document.body.appendChild(scriptElement);
});
}
and then call and subscribe this function in your ts file to know when it is loaded or do something when its loaded in your page.
im trying to implement this
https://codepen.io/Mertl/pen/XWdyRwJ into a hugo template https://themes.gohugo.io/somrat/ ;
I dont know where to put the html file; put only the no cursor part into style.css; and copy the .js into script.js, nothing happens on the localhost website when I do this, any help is appreciated <3
code: (html, css and js)
<canvas id="canvas"></canvas>
body,
html {
margin: 0px;
padding: 0px;
position: fixed;
background: rgb(30, 30, 30);
cursor: none;
}
window.onload = function () {
//functions definition
//class definition
class segm {
constructor(x, y, l) {
this.b = Math.random()*1.9+0.1;
this.x0 = x;
this.y0 = y;
this.a = Math.random() * 2 * Math.PI;
this.x1 = this.x0 + l * Math.cos(this.a);
this.y1 = this.y0 + l * Math.sin(this.a);
this.l = l;
}
update(x, y) {
this.x0 = x;
this.y0 = y;
this.a = Math.atan2(this.y1 - this.y0, this.x1 - this.x0);
this.x1 = this.x0 + this.l * Math.cos(this.a);
this.y1 = this.y0 + this.l * Math.sin(this.a);
}
}
class rope {
constructor(tx, ty, l, b, slq, typ) {
if(typ == "l"){
this.res = l / 2;
}else{
this.res = l / slq;
}
this.type = typ;
this.l = l;
this.segm = [];
this.segm.push(new segm(tx, ty, this.l / this.res));
for (let i = 1; i < this.res; i++) {
this.segm.push(
new segm(this.segm[i - 1].x1, this.segm[i - 1].y1, this.l / this.res)
);
}
this.b = b;
}
update(t) {
this.segm[0].update(t.x, t.y);
for (let i = 1; i < this.res; i++) {
this.segm[i].update(this.segm[i - 1].x1, this.segm[i - 1].y1);
}
}
show() {
if(this.type == "l"){
c.beginPath();
for (let i = 0; i < this.segm.length; i++) {
c.lineTo(this.segm[i].x0, this.segm[i].y0);
}
c.lineTo(
this.segm[this.segm.length - 1].x1,
this.segm[this.segm.length - 1].y1
);
c.strokeStyle = "white";
c.lineWidth = this.b;
c.stroke();
c.beginPath();
c.arc(this.segm[0].x0, this.segm[0].y0, 1, 0, 2 * Math.PI);
c.fillStyle = "white";
c.fill();
c.beginPath();
c.arc(
this.segm[this.segm.length - 1].x1,
this.segm[this.segm.length - 1].y1,
2,
0,
2 * Math.PI
);
c.fillStyle = "white";
c.fill();
}else{
for (let i = 0; i < this.segm.length; i++) {
c.beginPath();
c.arc(this.segm[i].x0, this.segm[i].y0, this.segm[i].b, 0, 2*Math.PI);
c.fillStyle = "white";
c.fill();
}
c.beginPath();
c.arc(
this.segm[this.segm.length - 1].x1,
this.segm[this.segm.length - 1].y1,
2, 0, 2*Math.PI
);
c.fillStyle = "white";
c.fill();
}
}
}
//setting up canvas
let c = init("canvas").c,
canvas = init("canvas").canvas,
w = (canvas.width = window.innerWidth),
h = (canvas.height = window.innerHeight),
ropes = [];
//variables definition
let nameOfVariable = "value",
mouse = {},
last_mouse = {},
rl = 50,
randl = [],
target = { x: w/2, y: h/2 },
last_target = {},
t = 0,
q = 10,
da = [],
type = "l";
for (let i = 0; i < 100; i++) {
if(Math.random() > 0.25){
type = "l";
}else{
type = "o";
}
ropes.push(
new rope(
w / 2,
h / 2,
(Math.random() * 1 + 0.5) * 500,
Math.random() * 0.4 + 0.1,
Math.random()*15+5,
type
)
);
randl.push(Math.random() * 2 - 1);
da.push(0);
}
//place for objects in animation
function draw() {
if (mouse.x) {
target.errx = mouse.x - target.x;
target.erry = mouse.y - target.y;
} else {
target.errx =
w / 2 +
(h / 2 - q) *
Math.sqrt(2) *
Math.cos(t) /
(Math.pow(Math.sin(t), 2) + 1) -
target.x;
target.erry =
h / 2 +
(h / 2 - q) *
Math.sqrt(2) *
Math.cos(t) *
Math.sin(t) /
(Math.pow(Math.sin(t), 2) + 1) -
target.y;
}
target.x += target.errx / 10;
target.y += target.erry / 10;
t += 0.01;
for (let i = 0; i < ropes.length; i++) {
if (randl[i] > 0) {
da[i] += (1 - randl[i]) / 10;
} else {
da[i] += (-1 - randl[i]) / 10;
}
ropes[i].update({
x:
target.x +
randl[i] * rl * Math.cos((i * 2 * Math.PI) / ropes.length + da[i]),
y:
target.y +
randl[i] * rl * Math.sin((i * 2 * Math.PI) / ropes.length + da[i])
});
ropes[i].show();
}
last_target.x = target.x;
last_target.y = target.y;
}
//mouse position
canvas.addEventListener(
"mousemove",
function (e) {
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop;
},
false
);
canvas.addEventListener("mouseleave", function(e) {
mouse.x = false;
mouse.y = false;
});
//animation frame
function loop() {
window.requestAnimFrame(loop);
c.clearRect(0, 0, w, h);
draw();
}
//window resize
window.addEventListener("resize", function () {
(w = canvas.width = window.innerWidth),
(h = canvas.height = window.innerHeight);
loop();
});
//animation runner
loop();
setInterval(loop, 1000 / 60);
};
Try to implement like this
window.init = function(elemid) {
let canvas = document.getElementById(elemid),
c = canvas.getContext("2d"),
w = (canvas.width = window.innerWidth),
h = (canvas.height = window.innerHeight);
c.fillStyle = "rgba(30,30,30,1)";
c.fillRect(0, 0, w, h);
return {c:c,canvas:canvas};
}
window.requestAnimFrame = function() {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback);
}
);
};
window.spaceworm = function () {
//functions definition
//class definition
class segm {
constructor(x, y, l) {
this.b = Math.random()*1.9+0.1;
this.x0 = x;
this.y0 = y;
this.a = Math.random() * 2 * Math.PI;
this.x1 = this.x0 + l * Math.cos(this.a);
this.y1 = this.y0 + l * Math.sin(this.a);
this.l = l;
}
update(x, y) {
this.x0 = x;
this.y0 = y;
this.a = Math.atan2(this.y1 - this.y0, this.x1 - this.x0);
this.x1 = this.x0 + this.l * Math.cos(this.a);
this.y1 = this.y0 + this.l * Math.sin(this.a);
}
}
class rope {
constructor(tx, ty, l, b, slq, typ) {
if(typ == "l"){
this.res = l / 2;
}else{
this.res = l / slq;
}
this.type = typ;
this.l = l;
this.segm = [];
this.segm.push(new segm(tx, ty, this.l / this.res));
for (let i = 1; i < this.res; i++) {
this.segm.push(
new segm(this.segm[i - 1].x1, this.segm[i - 1].y1, this.l / this.res)
);
}
this.b = b;
}
update(t) {
this.segm[0].update(t.x, t.y);
for (let i = 1; i < this.res; i++) {
this.segm[i].update(this.segm[i - 1].x1, this.segm[i - 1].y1);
}
}
show() {
if(this.type == "l"){
c.beginPath();
for (let i = 0; i < this.segm.length; i++) {
c.lineTo(this.segm[i].x0, this.segm[i].y0);
}
c.lineTo(
this.segm[this.segm.length - 1].x1,
this.segm[this.segm.length - 1].y1
);
c.strokeStyle = "white";
c.lineWidth = this.b;
c.stroke();
c.beginPath();
c.arc(this.segm[0].x0, this.segm[0].y0, 1, 0, 2 * Math.PI);
c.fillStyle = "white";
c.fill();
c.beginPath();
c.arc(
this.segm[this.segm.length - 1].x1,
this.segm[this.segm.length - 1].y1,
2,
0,
2 * Math.PI
);
c.fillStyle = "white";
c.fill();
}else{
for (let i = 0; i < this.segm.length; i++) {
c.beginPath();
c.arc(this.segm[i].x0, this.segm[i].y0, this.segm[i].b, 0, 2*Math.PI);
c.fillStyle = "white";
c.fill();
}
c.beginPath();
c.arc(
this.segm[this.segm.length - 1].x1,
this.segm[this.segm.length - 1].y1,
2, 0, 2*Math.PI
);
c.fillStyle = "white";
c.fill();
}
}
}
//setting up canvas
let c = init("canvas").c,
canvas = init("canvas").canvas,
w = (canvas.width = window.innerWidth),
h = (canvas.height = window.innerHeight),
ropes = [];
//variables definition
let nameOfVariable = "value",
mouse = {},
last_mouse = {},
rl = 50,
randl = [],
target = { x: w/2, y: h/2 },
last_target = {},
t = 0,
q = 10,
da = [],
type = "l";
for (let i = 0; i < 100; i++) {
if(Math.random() > 0.25){
type = "l";
}else{
type = "o";
}
ropes.push(
new rope(
w / 2,
h / 2,
(Math.random() * 1 + 0.5) * 500,
Math.random() * 0.4 + 0.1,
Math.random()*15+5,
type
)
);
randl.push(Math.random() * 2 - 1);
da.push(0);
}
//place for objects in animation
function draw() {
if (mouse.x) {
target.errx = mouse.x - target.x;
target.erry = mouse.y - target.y;
} else {
target.errx =
w / 2 +
(h / 2 - q) *
Math.sqrt(2) *
Math.cos(t) /
(Math.pow(Math.sin(t), 2) + 1) -
target.x;
target.erry =
h / 2 +
(h / 2 - q) *
Math.sqrt(2) *
Math.cos(t) *
Math.sin(t) /
(Math.pow(Math.sin(t), 2) + 1) -
target.y;
}
target.x += target.errx / 10;
target.y += target.erry / 10;
t += 0.01;
for (let i = 0; i < ropes.length; i++) {
if (randl[i] > 0) {
da[i] += (1 - randl[i]) / 10;
} else {
da[i] += (-1 - randl[i]) / 10;
}
ropes[i].update({
x:
target.x +
randl[i] * rl * Math.cos((i * 2 * Math.PI) / ropes.length + da[i]),
y:
target.y +
randl[i] * rl * Math.sin((i * 2 * Math.PI) / ropes.length + da[i])
});
ropes[i].show();
}
last_target.x = target.x;
last_target.y = target.y;
}
//mouse position
canvas.addEventListener(
"mousemove",
function (e) {
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop;
},
false
);
canvas.addEventListener("mouseleave", function(e) {
mouse.x = false;
mouse.y = false;
});
//animation frame
function loop() {
window.requestAnimFrame(loop);
c.clearRect(0, 0, w, h);
draw();
}
//window resize
window.addEventListener("resize", function () {
(w = canvas.width = window.innerWidth),
(h = canvas.height = window.innerHeight);
loop();
});
//animation runner
loop();
setInterval(loop, 1000 / 60);
};
window.onload = spaceworm;
body,
html {
margin: 0px;
padding: 0px;
position: fixed;
background: rgb(30, 30, 30);
cursor: none;
}
<canvas id="canvas"></canvas>
i'd like to start this confetti animation only after user clicked on a button.
here is a link co codepen:https://codepen.io/gamanox/pen/FkEbH?page=1&
var COLORS, Confetti, NUM_CONFETTI, PI_2, canvas, confetti, context, drawCircle, drawCircle2, drawCircle3, i, range, xpos;
NUM_CONFETTI = 60;
COLORS = [[255, 255, 255], [255, 144, 0], [255, 255, 255], [255, 144, 0], [0, 277, 235]];
PI_2 = 2 * Math.PI;
canvas = document.getElementById("confeti");
context = canvas.getContext("2d");
window.w = 0;
window.h = 0;
window.resizeWindow = function() {
window.w = canvas.width = window.innerWidth;
return window.h = canvas.height = window.innerHeight;
};
window.addEventListener('resize', resizeWindow, false);
window.onload = function() {
return setTimeout(resizeWindow, 0);
};
range = function(a, b) {
return (b - a) * Math.random() + a;
};
drawCircle = function(x, y, r, style) {
context.beginPath();
context.moveTo(x, y);
context.bezierCurveTo(x - 17, y + 14, x + 13, y + 5, x - 5, y + 22);
context.lineWidth = 3;
context.strokeStyle = style;
return context.stroke();
};
drawCircle2 = function(x, y, r, style) {
context.beginPath();
context.moveTo(x, y);
context.lineTo(x + 10, y + 10);
context.lineTo(x + 10, y);
context.closePath();
context.fillStyle = style;
return context.fill();
};
drawCircle3 = function(x, y, r, style) {
context.beginPath();
context.moveTo(x, y);
context.lineTo(x + 10, y + 10);
context.lineTo(x + 10, y);
context.closePath();
context.fillStyle = style;
return context.fill();
};
xpos = 0.9;
document.onmousemove = function(e) {
return xpos = e.pageX / w;
};
window.requestAnimationFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
return window.setTimeout(callback, 100 / 20);
};
})();
Confetti = (function() {
function Confetti() {
this.style = COLORS[~~range(0, 5)];
this.rgb = "rgba(" + this.style[0] + "," + this.style[1] + "," + this.style[2];
this.r = ~~range(2, 6);
this.r2 = 2 * this.r;
this.replace();
}
Confetti.prototype.replace = function() {
this.opacity = 0;
this.dop = 0.03 * range(1, 4);
this.x = range(-this.r2, w - this.r2);
this.y = range(-20, h - this.r2);
this.xmax = w - this.r;
this.ymax = h - this.r;
this.vx = range(0, 2) + 8 * xpos - 5;
return this.vy = 0.7 * this.r + range(-1, 1);
};
Confetti.prototype.draw = function() {
var ref;
this.x += this.vx;
this.y += this.vy;
this.opacity += this.dop;
if (this.opacity > 1) {
this.opacity = 1;
this.dop *= -1;
}
if (this.opacity < 0 || this.y > this.ymax) {
this.replace();
}
if (!((0 < (ref = this.x) && ref < this.xmax))) {
this.x = (this.x + this.xmax) % this.xmax;
}
drawCircle(~~this.x, ~~this.y, this.r, this.rgb + "," + this.opacity + ")");
drawCircle3(~~this.x * 0.5, ~~this.y, this.r, this.rgb + "," + this.opacity + ")");
return drawCircle2(~~this.x * 1.5, ~~this.y * 1.5, this.r, this.rgb + "," + this.opacity + ")");
};
return Confetti;
})();
confetti = (function() {
var j, ref, results;
results = [];
for (i = j = 1, ref = NUM_CONFETTI; 1 <= ref ? j <= ref : j >= ref; i = 1 <= ref ? ++j : --j) {
results.push(new Confetti);
}
return results;
})();
window.step = function() {
var c, j, len, results;
requestAnimationFrame(step);
context.clearRect(0, 0, w, h);
results = [];
for (j = 0, len = confetti.length; j < len; j++) {
c = confetti[j];
results.push(c.draw());
}
return results;
};
step();
i tried to wrap the following code into a function and then call it with jquery on click but it does not work. any suggestions would be highly appreciated
Add a click listener to the document, and run step() inside of it:
//step()
document.addEventListener "click", () =>
step()
https://codepen.io/jdoyle/pen/mMpQKR
This works, but if you click more than once, you get some weird results. Do a little refactoring and remove the event listener once the user clicks:
// step()
start = ->
requestAnimationFrame(step)
document.removeEventListener "click", start
document.addEventListener "click", start
Replace:
confetti = (function() {
var j, ref, results;
results = [];
for (i = j = 1, ref = NUM_CONFETTI; 1 <= ref ? j <= ref : j >= ref; i = 1 <= ref ? ++j : --j) {
results.push(new Confetti);
}
return results;
})();
to
confetti = [];
Put inside click listener:
var j, ref;
for (i = j = 1, ref = NUM_CONFETTI; 1 <= ref ? j <= ref : j >= ref; i = 1 <= ref ? ++j : --j) {
confetti.push(new Confetti);
}
I'm attempting to teach myself how to work with canvas — and I'm probably in way over my head — but thought I'd ask if anyone has a solution to this issue I came across.
As a lesson I decided to try to start with this metaball idea:
http://codepen.io/ge1doot/pen/RNdwQB
and rework it so the metaballs are stationary but move upwards at varying rates as one scrolls down the page.
I sort of got it working here — displays much better on the fiddle than below.
https://jsfiddle.net/L7cr46px/2/
function getScrollOffsets() {
var doc = document, w = window;
var x, y, docEl;
if ( typeof w.pageYOffset === 'number' ) {
x = w.pageXOffset;
y = w.pageYOffset;
} else {
docEl = (doc.compatMode && doc.compatMode === 'CSS1Compat')?
doc.documentElement: doc.body;
x = docEl.scrollLeft;
y = docEl.scrollTop;
}
return {x:x, y:y};
}
var lava, ballData;
ballData = new Array();
(function() {
var metablobby = metablobby || {
screen: {
elem: null,
callback: null,
ctx: null,
width: 0,
height: 0,
left: 0,
top: 0,
init: function (id, callback, initRes) {
this.elem = document.getElementById(id);
this.callback = callback || null;
if (this.elem.tagName == "CANVAS") this.ctx = this.elem.getContext("2d");
window.addEventListener('resize', function () {
this.resize();
}.bind(this), false);
this.elem.onselectstart = function () { return false; }
this.elem.ondrag = function () { return false; }
initRes && this.resize();
return this;
},
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;
}
this.callback && this.callback();
},
pointer: {
screen: null,
elem: null,
callback: null,
pos: {x:0, y:0},
mov: {x:0, y:0},
drag: {x:0, y:0},
start: {x:0, y:0},
end: {x:0, y:0},
active: false,
touch: false,
move: function (e, touch) {
//this.active = true;
this.touch = touch;
e.preventDefault();
var pointer = touch ? e.touches[0] : e;
this.mov.x = pointer.clientX - this.screen.left;
this.mov.y = pointer.clientY - this.screen.top;
if (this.active) {
this.pos.x = this.mov.x;
this.pos.y = this.mov.y;
this.drag.x = this.end.x - (this.pos.x - this.start.x);
this.drag.y = this.end.y - (this.pos.y - this.start.y);
this.callback.move && this.callback.move();
}
},
scroll: function(e, touch){
run();
},
init: function (callback) {
this.screen = metablobby.screen;
this.elem = this.screen.elem;
this.callback = callback || {};
if ('ontouchstart' in window) {
// touch
this.elem.ontouchstart = function (e) { this.down(e, true); }.bind(this);
this.elem.ontouchmove = function (e) { this.move(e, true); }.bind(this);
this.elem.ontouchend = function (e) { this.up(e, true); }.bind(this);
this.elem.ontouchcancel = function (e) { this.up(e, true); }.bind(this);
}
document.addEventListener("mousemove", function (e) { this.move(e, false); }.bind(this), true);
document.addEventListener("scroll", function (e) { this.scroll(e, false); }.bind(this), true);
return this;
}
},
}
}
// ==== Point constructor ====
var Point = function(x, y) {
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);
}
// ==== Ball constructor ====
var Ball = function(parent,i) {
var x = Math.floor(Math.random() * window.innerWidth) + 1;
var y = Math.floor(Math.random() * (window.innerHeight)*5) + 1;
var radius = (Math.floor(Math.random() * 65) + 15)
var drift = Math.random();
ballData[i]=[x,y,radius, drift];
this.vel = new Point(0,0);
this.pos = new Point(x,y);
this.size = radius;
this.width = parent.width;
this.height = parent.height;
}
// ==== move balls ====
Ball.prototype.move = function(i) {
// ---- interact with pointer ----
if (pointer.active) {
var dx = pointer.pos.x - this.pos.x;
var dy = pointer.pos.y - this.pos.y;
var a = Math.atan2(dy, dx);
var v = -Math.min(
10,
500 / Math.sqrt(dx * dx + dy * dy)
);
this.pos = this.pos.add(
new Point(
Math.cos(a) * v,
Math.sin(a) * v
)
);
}
var drift = ballData[i-1][3];
var pageOffset = getScrollOffsets().y;
this.pos.y = ballData[i-1][1] - (pageOffset*drift);
this.vel.y = 0 - (pageOffset*drift);
this.pos = this.pos.add(this.vel);
}
// ==== lavalamp constructor ====
var LavaLamp = function(width, height, numBalls) {
this.step = 4;
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.paint = false;
this.metaFill = '#000000';
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 i = 0; i < 50; i++) {
this.balls[i] = new Ball(this,i);
}
}
// ==== compute cell force ====
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) {
var force = 0.6 * this.sign;
} else {
var cell = this.grid[id];
var force = 0;
var i = 0,
ball;
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;
}
// ---- compute cell ----
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(typeof this.grid[id] !== "undefined"){
if (this.grid[id].computed === this.iter) return false;
var dir, 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
);
ctx.lineTo(
this.grid[(x + this.plx[4 * dir + 0]) + (y + this.ply[4 * dir + 0]) * (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, ball;
while (ball = this.balls[i++]) ball.move(i);
// ---- reset grid ----
this.iter++;
this.sign = -this.sign;
this.paint = false;
ctx.fillStyle = '#FF0000';
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 ----
do {
next = this.marchingSquares(next);
} while (next);
// ---- fill and close path ----
if (this.paint) {
ctx.fill();
ctx.closePath();
ctx.beginPath();
this.paint = false;
}
}
}
// ==== main loop ====
var run = function() {
//requestAnimationFrame(run);
ctx.clearRect(0, 0, screen.width, screen.height);
lava.renderMetaballs();
}
// ---- canvas ----
var screen = metablobby.screen.init("thedots", null, true),
ctx = screen.ctx,
pointer = screen.pointer.init();
screen.resize();
// ---- create LavaLamps ----
lava = new LavaLamp(screen.width, screen.height, 10);
// ---- start engine ----
run();
})();
html,body {
height: 100%;
width: 100%;
background-color: white;
}
body {
height: 500vh;
}
#thedots {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 100;
pointer-events: none;
}
<canvas id="thedots" width="1203" height="363"></canvas>
but I'm getting insane rendering bugs as you'll see. Or maybe they're not rendering bugs and just some faulty code. Any help would be much appreciated.
Thanks!