Canvas Marching Squares Glitch / Scroll Integration - javascript

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!

Related

JS canva code has horrible performance on firefox, works fine on chomium

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 };
}

Canvas particle animation mobile touch event with elements positioned on top

I have this canvas particle animation that follows the mouse event correctly but it does not follow a touch event on mobile. I realized that it's because I have this animation beneath the rest of the content which is positioned on top. When there is something on top of the canvas that it's hitting, it does not trigger the touch event. I'm hoping someone can help me figure out how to avoid this issue so that the animation will still follow the user underneath the page content.
JS
var PI2 = Math.PI * 2;
var HALF_PI = Math.PI / 2;
var isTouch = 'ontouchstart' in window;
var isSafari = !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/);
function Canvas(options) {
options = _.clone(options || {});
this.options = _.defaults(options, this.options);
this.el = this.options.el;
this.ctx = this.el.getContext('2d');
this.dpr = window.devicePixelRatio || 1;
this.updateDimensions();
window.addEventListener('resize', this.updateDimensions.bind(this), false);
this.resetTarget();
if(isTouch){
// touch
this.el.addEventListener('touchstart', this.touchMove.bind(this), false);
this.el.addEventListener('touchmove', this.touchMove.bind(this), false);
// this.el.addEventListener('touchend', this.resetTarget.bind(this), false);
} else {
// Mouse
window.addEventListener('mousemove', this.mouseMove.bind(this), false);
window.addEventListener('mouseout', this.resetTarget.bind(this), false);
}
this.setupParticles();
this.loop();
}
Canvas.prototype.updateDimensions = function() {
this.width = this.el.width = _.result(this.options, 'width') * this.dpr;
this.height = this.el.height = _.result(this.options, 'height') * this.dpr;
this.el.style.width = _.result(this.options, 'width') + 'px';
this.el.style.height = _.result(this.options, 'height') + 'px';
}
// Update the orb target
Canvas.prototype.mouseMove = function(event) {
this.target = new Vector(event.clientX * this.dpr, event.clientY* this.dpr);
}
// Reset to center when we mouse out
Canvas.prototype.resetTarget = function() {
this.target = new Vector(this.width / 2, this.height /2);
}
// Touch Eent
Canvas.prototype.touchMove = function(event) {
if(event.touches.length === 1) { event.preventDefault(); }
this.target = new Vector(event.touches[0].pageX * this.dpr, event.touches[0].pageY * this.dpr);
}
// Defaults
Canvas.prototype.options = {
count: 11,
speed: 0.001,
width: 400,
height: 400,
size: 5,
radius: 1,
background: '240, 240, 240, 0.6',
maxDistance: 100
}
Canvas.prototype.setupParticles = function() {
this.particles = [];
var index = -1;
var between = PI2 / this.options.count;
while(++index < this.options.count) {
var x;
var y;
var angle;
var max = Math.max(this.width, this.height);
angle = (index + 1) * between;
x = Math.cos(angle) * max;
x += this.width / 2;
y = Math.sin(angle) * max;
y += this.height / 2;
var particle = new Particle({
x: x,
y: y,
radius: this.options.radius,
size: this.options.size,
angle: angle,
color: this.options.color
});
this.particles.push(particle);
}
}
Canvas.prototype.findClosest = function() {
var index = -1;
var pointsLength = this.particles.length;
while(++index < pointsLength) {
var closestIndex = -1;
this.particles[index].closest = [];
while(++closestIndex < pointsLength) {
var closest = this.particles[closestIndex];
var distance = this.particles[index].position.distanceTo(closest.position);
if(distance < this.options.maxDistance) {
var vector = new Vector(closest.position.x, closest.position.y);
vector.opacity = 1 - (distance / this.options.maxDistance);
vector.distance = distance;
this.particles[index].closest.push(vector);
}
}
}
}
Canvas.prototype.loop = function() {
// this.clear();
if(isTouch || isSafari) {
this.ghost();
} else {
this.ghostGradient();
}
if(this.options.maxDistance > 0) {
this.findClosest();
}
this.draw();
window.requestAnimationFrame(_.bind(this.loop, this));
}
Canvas.prototype.clear = function() {
this.ctx.clearRect(0, 0 , this.width, this.height);
}
Canvas.prototype.ghost = function() {
this.ctx.globalCompositeOperation = "source-over";
this.ctx.rect(0, 0 , this.width, this.height);
if(typeof this.options.background === 'string') {
this.ctx.fillStyle = "rgba(" + this.options.background + ")";
} else {
this.ctx.fillStyle = "rgba(" + this.options.background[0] + ")";
}
this.ctx.fill();
}
Canvas.prototype.ghostGradient = function() {
var gradient;
if(typeof this.options.background === 'string') {
this.ctx.fillStyle = 'rgba(' + this.options.background + ')';
} else {
var gradient = this.ctx.createLinearGradient(0, 0, 0, this.height);
var length = this.options.background.length;
for(var i = 0; i < length; i++){
gradient.addColorStop((i+1) / length, 'rgba(' + this.options.background[i] + ')');
}
this.ctx.fillStyle = gradient;
}
this.ctx.globalOpacity = 0.1;
this.ctx.globalCompositeOperation = "darken";
this.ctx.fillRect(0, 0 , this.width, this.height);
}
// Draw
Canvas.prototype.draw = function() {
var index = -1;
var length = this.particles.length;
while(++index < length) {
var point = this.particles[index];
var color = point.color || this.options.color;
point.update(this.target, index);
this.ctx.globalAlpha = 0.3;
this.ctx.globalCompositeOperation = "lighten";
this.ctx.fillStyle = 'rgb(' + color + ')';
this.ctx.beginPath();
this.ctx.arc(point.position.x, point.position.y, point.size, 0, PI2, false);
this.ctx.closePath();
this.ctx.fill();
if(this.options.maxDistance > 0) {
this.drawLines(point, color);
}
}
}
// Draw connecting lines
Canvas.prototype.drawLines = function (point, color) {
color = color || this.options.color;
var index = -1;
var length = point.closest.length;
this.ctx.globalAlpha = 0.2;
this.ctx.globalCompositeOperation = "screen";
this.ctx.lineCap = 'round';
while(++index < length) {
this.ctx.lineWidth = (point.size * 2) * point.closest[index].opacity;
this.ctx.strokeStyle = 'rgba(250,250,250, ' + point.closest[index].opacity + ')';
this.ctx.beginPath();
this.ctx.moveTo(point.position.x, point.position.y);
this.ctx.lineTo(point.closest[index].x, point.closest[index].y);
this.ctx.stroke();
}
}
function Particle(options) {
options = _.clone(options || {});
this.options = _.defaults(options, this.options);
this.position = this.shift = new Vector(this.options.x, this.options.y);
this.speed = this.options.speed || 0.01 + Math.random() * 0.04;
this.angle = this.options.angle || 0;
if(this.options.color) {
var color = this.options.color.split(',');
var colorIndex = -1;
while(++colorIndex < 3) {
color[colorIndex] = Math.round(parseInt(color[colorIndex], 10) + (Math.random()*100)-50);
// Clamp
color[colorIndex] = Math.min(color[colorIndex], 255);
color[colorIndex] = Math.max(color[colorIndex], 0);
}
this.color = color.join(', ');
}
// Size
this.options.size = this.options.size || 7;
this.size = 1 + Math.random() * this.options.size;
this.targetSize = this.options.targetSize || this.options.size;
this.orbit = this.options.radius * 0.5 + (this.options.radius * 0.5 * Math.random());
}
Particle.prototype.update = function(target, index) {
this.angle += this.speed;
this.shift.x += (target.x - this.shift.x) * this.speed;
this.shift.y += (target.y - this.shift.y) * this.speed;
this.position.x = this.shift.x + Math.cos(index + this.angle) * this.orbit;
this.position.y = this.shift.y + Math.sin(index + this.angle) * this.orbit;
if(!isSafari) {
this.size += (this.targetSize - this.size) * 0.03;
if(Math.round(this.size) === Math.round(this.targetSize)) {
this.targetSize = 1 + Math.random() * this.options.size;
}
}
}
function Vector(x, y) {
this.x = x || 0;
this.y = y || 0;
}
Vector.prototype.distanceTo = function(vector, abs) {
var distance = Math.sqrt(Math.pow(this.x - vector.x, 2) + Math.pow(this.y - vector.y, 2));
return abs || false ? Math.abs(distance) : distance;
};
new Canvas({
el: document.getElementById('canvas'),
count: 25,
speed: 0.3,
radius: 6,
width: function() { return window.innerWidth; },
height: function() { return window.innerHeight; },
size: 15,
color: '30, 180, 1',
maxDistance: 100,
background: ['250,250,250,1', '215,216,215,0.8']
})
CSS
html, body {
min-height: 100%;
height: 100%;
}
#canvas {
width: 100%;
height: 100%;
position: fixed;
z-index: 100;
}
.page-content {
position: relative;
z-index: 900;
display: flex;
flex-direction: column;
}
HTML
<body>
<div id="container">
<canvas id="canvas"></canvas>
<div class="page-content">
</div>
</div>
</body>

How to add JavaScript file in angular cli application?

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.

How to change opacity in Javascript for object

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>

How to get rid of gray overlay on Three.js transition?

I have a transition between two pngs using three.js. The images have a white background, but they appear to have a gray tint. I know it is probably something obvious but I can't find where in the code this is specified. How do I get rid of this? Here is a demo on Codepen:
https://codepen.io/mastroneel/pen/Oazzyo
window.onload = init;
console.ward = function() {}; // what warnings?
function init() {
var root = new THREERoot({
createCameraControls: !true,
antialias: (window.devicePixelRatio === 1),
fov: 80
});
root.renderer.setClearColor(0x000000, 0);
root.renderer.setPixelRatio(window.devicePixelRatio || 1);
root.camera.position.set(0, 0, 60);
var width = 100;
var height = 100;
var slide = new Slide(width, height, 'out');
var l1 = new THREE.ImageLoader();
l1.setCrossOrigin('Anonymous');
slide.setImage(l1.load('https://image.ibb.co/f6mVsA/helmet.png'));
root.scene.add(slide);
var slide2 = new Slide(width, height, 'in');
var l2 = new THREE.ImageLoader();
l2.setCrossOrigin('Anonymous');
slide2.setImage(l2.load('https://image.ibb.co/mb1KkV/player.png'));
root.scene.add(slide2);
var tl = new TimelineMax({
repeat: -1,
repeatDelay: 1.0,
yoyo: true
});
tl.add(slide.transition(), 0);
tl.add(slide2.transition(), 0);
createTweenScrubber(tl);
window.addEventListener('keyup', function(e) {
if (e.keyCode === 80) {
tl.paused(!tl.paused());
}
});
}
////////////////////
// CLASSES
////////////////////
function Slide(width, height, animationPhase) {
var plane = new THREE.PlaneGeometry(width, height, width * 2, height * 2);
THREE.BAS.Utils.separateFaces(plane);
var geometry = new SlideGeometry(plane);
geometry.bufferUVs();
var aAnimation = geometry.createAttribute('aAnimation', 2);
var aStartPosition = geometry.createAttribute('aStartPosition', 3);
var aControl0 = geometry.createAttribute('aControl0', 3);
var aControl1 = geometry.createAttribute('aControl1', 3);
var aEndPosition = geometry.createAttribute('aEndPosition', 3);
var i, i2, i3, i4, v;
var minDuration = 0.8;
var maxDuration = 1.2;
var maxDelayX = 0.9;
var maxDelayY = 0.125;
var stretch = 0.11;
this.totalDuration = maxDuration + maxDelayX + maxDelayY + stretch;
var startPosition = new THREE.Vector3();
var control0 = new THREE.Vector3();
var control1 = new THREE.Vector3();
var endPosition = new THREE.Vector3();
var tempPoint = new THREE.Vector3();
function getControlPoint0(centroid) {
var signY = Math.sign(centroid.y);
tempPoint.x = THREE.Math.randFloat(0.1, 0.3) * 50;
tempPoint.y = signY * THREE.Math.randFloat(0.1, 0.3) * 70;
tempPoint.z = THREE.Math.randFloatSpread(20);
return tempPoint;
}
function getControlPoint1(centroid) {
var signY = Math.sign(centroid.y);
tempPoint.x = THREE.Math.randFloat(0.3, 0.6) * 50;
tempPoint.y = -signY * THREE.Math.randFloat(0.3, 0.6) * 70;
tempPoint.z = THREE.Math.randFloatSpread(20);
return tempPoint;
}
for (i = 0, i2 = 0, i3 = 0, i4 = 0; i < geometry.faceCount; i++, i2 += 6, i3 += 9, i4 += 12) {
var face = plane.faces[i];
var centroid = THREE.BAS.Utils.computeCentroid(plane, face);
// animation
var duration = THREE.Math.randFloat(minDuration, maxDuration);
var delayX = THREE.Math.mapLinear(centroid.x, -width * 0.5, width * 0.5, 0.0, maxDelayX);
var delayY;
if (animationPhase === 'in') {
delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, 0.0, maxDelayY)
} else {
delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, maxDelayY, 0.0)
}
for (v = 0; v < 6; v += 2) {
aAnimation.array[i2 + v] = delayX + delayY + (Math.random() * stretch * duration);
aAnimation.array[i2 + v + 1] = duration;
}
// positions
endPosition.copy(centroid);
startPosition.copy(centroid);
if (animationPhase === 'in') {
control0.copy(centroid).sub(getControlPoint0(centroid));
control1.copy(centroid).sub(getControlPoint1(centroid));
} else { // out
control0.copy(centroid).add(getControlPoint0(centroid));
control1.copy(centroid).add(getControlPoint1(centroid));
}
for (v = 0; v < 9; v += 3) {
aStartPosition.array[i3 + v] = startPosition.x;
aStartPosition.array[i3 + v + 1] = startPosition.y;
aStartPosition.array[i3 + v + 2] = startPosition.z;
aControl0.array[i3 + v] = control0.x;
aControl0.array[i3 + v + 1] = control0.y;
aControl0.array[i3 + v + 2] = control0.z;
aControl1.array[i3 + v] = control1.x;
aControl1.array[i3 + v + 1] = control1.y;
aControl1.array[i3 + v + 2] = control1.z;
aEndPosition.array[i3 + v] = endPosition.x;
aEndPosition.array[i3 + v + 1] = endPosition.y;
aEndPosition.array[i3 + v + 2] = endPosition.z;
}
}
var material = new THREE.BAS.BasicAnimationMaterial({
shading: THREE.FlatShading,
side: THREE.DoubleSide,
uniforms: {
uTime: {
type: 'f',
value: 0
}
},
shaderFunctions: [
THREE.BAS.ShaderChunk['cubic_bezier'],
//THREE.BAS.ShaderChunk[(animationPhase === 'in' ? 'ease_out_cubic' : 'ease_in_cubic')],
THREE.BAS.ShaderChunk['ease_in_out_cubic'],
THREE.BAS.ShaderChunk['quaternion_rotation']
],
shaderParameters: [
'uniform float uTime;',
'attribute vec2 aAnimation;',
'attribute vec3 aStartPosition;',
'attribute vec3 aControl0;',
'attribute vec3 aControl1;',
'attribute vec3 aEndPosition;',
],
shaderVertexInit: [
'float tDelay = aAnimation.x;',
'float tDuration = aAnimation.y;',
'float tTime = clamp(uTime - tDelay, 0.0, tDuration);',
'float tProgress = ease(tTime, 0.0, 1.0, tDuration);'
//'float tProgress = tTime / tDuration;'
],
shaderTransformPosition: [
(animationPhase === 'in' ? 'transformed *= tProgress;' : 'transformed *= 1.0 - tProgress;'),
'transformed += cubicBezier(aStartPosition, aControl0, aControl1, aEndPosition, tProgress);'
]
}, {
map: new THREE.Texture(),
});
THREE.Mesh.call(this, geometry, material);
this.frustumCulled = false;
}
Slide.prototype = Object.create(THREE.Mesh.prototype);
Slide.prototype.constructor = Slide;
Object.defineProperty(Slide.prototype, 'time', {
get: function() {
return this.material.uniforms['uTime'].value;
},
set: function(v) {
this.material.uniforms['uTime'].value = v;
}
});
Slide.prototype.setImage = function(image) {
this.material.uniforms.map.value.image = image;
this.material.uniforms.map.value.needsUpdate = true;
};
Slide.prototype.transition = function() {
return TweenMax.fromTo(this, 3.0, {
time: 0.0
}, {
time: this.totalDuration,
ease: Power0.easeInOut
});
};
function SlideGeometry(model) {
THREE.BAS.ModelBufferGeometry.call(this, model);
}
SlideGeometry.prototype = Object.create(THREE.BAS.ModelBufferGeometry.prototype);
SlideGeometry.prototype.constructor = SlideGeometry;
SlideGeometry.prototype.bufferPositions = function() {
var positionBuffer = this.createAttribute('position', 3).array;
for (var i = 0; i < this.faceCount; i++) {
var face = this.modelGeometry.faces[i];
var centroid = THREE.BAS.Utils.computeCentroid(this.modelGeometry, face);
var a = this.modelGeometry.vertices[face.a];
var b = this.modelGeometry.vertices[face.b];
var c = this.modelGeometry.vertices[face.c];
positionBuffer[face.a * 3] = a.x - centroid.x;
positionBuffer[face.a * 3 + 1] = a.y - centroid.y;
positionBuffer[face.a * 3 + 2] = a.z - centroid.z;
positionBuffer[face.b * 3] = b.x - centroid.x;
positionBuffer[face.b * 3 + 1] = b.y - centroid.y;
positionBuffer[face.b * 3 + 2] = b.z - centroid.z;
positionBuffer[face.c * 3] = c.x - centroid.x;
positionBuffer[face.c * 3 + 1] = c.y - centroid.y;
positionBuffer[face.c * 3 + 2] = c.z - centroid.z;
}
};
function THREERoot(params) {
params = utils.extend({
fov: 60,
zNear: 10,
zFar: 100000,
createCameraControls: true
}, params);
this.renderer = new THREE.WebGLRenderer({
antialias: params.antialias,
alpha: true
});
this.renderer.setPixelRatio(Math.min(2, window.devicePixelRatio || 1));
document.getElementById('three-container').appendChild(this.renderer.domElement);
this.camera = new THREE.PerspectiveCamera(
params.fov,
window.innerWidth / window.innerHeight,
params.zNear,
params.zfar
);
this.scene = new THREE.Scene();
if (params.createCameraControls) {
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
}
this.resize = this.resize.bind(this);
this.tick = this.tick.bind(this);
this.resize();
this.tick();
window.addEventListener('resize', this.resize, false);
}
THREERoot.prototype = {
tick: function() {
this.update();
this.render();
requestAnimationFrame(this.tick);
},
update: function() {
this.controls && this.controls.update();
},
render: function() {
this.renderer.render(this.scene, this.camera);
},
resize: function() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
};
////////////////////
// UTILS
////////////////////
var utils = {
extend: function(dst, src) {
for (var key in src) {
dst[key] = src[key];
}
return dst;
},
randSign: function() {
return Math.random() > 0.5 ? 1 : -1;
},
ease: function(ease, t, b, c, d) {
return b + ease.getRatio(t / d) * c;
},
fibSpherePoint: (function() {
var vec = {
x: 0,
y: 0,
z: 0
};
var G = Math.PI * (3 - Math.sqrt(5));
return function(i, n, radius) {
var step = 2.0 / n;
var r, phi;
vec.y = i * step - 1 + (step * 0.5);
r = Math.sqrt(1 - vec.y * vec.y);
phi = i * G;
vec.x = Math.cos(phi) * r;
vec.z = Math.sin(phi) * r;
radius = radius || 1;
vec.x *= radius;
vec.y *= radius;
vec.z *= radius;
return vec;
}
})(),
spherePoint: (function() {
return function(u, v) {
u === undefined && (u = Math.random());
v === undefined && (v = Math.random());
var theta = 2 * Math.PI * u;
var phi = Math.acos(2 * v - 1);
var vec = {};
vec.x = (Math.sin(phi) * Math.cos(theta));
vec.y = (Math.sin(phi) * Math.sin(theta));
vec.z = (Math.cos(phi));
return vec;
}
})()
};
function createTweenScrubber(tween, seekSpeed) {
seekSpeed = seekSpeed || 0.001;
function stop() {
TweenMax.to(tween, 1, {
timeScale: 0
});
}
function resume() {
TweenMax.to(tween, 1, {
timeScale: 1
});
}
function seek(dx) {
var progress = tween.progress();
var p = THREE.Math.clamp((progress + (dx * seekSpeed)), 0, 1);
tween.progress(p);
}
var _cx = 0;
// desktop
var mouseDown = false;
document.body.style.cursor = 'pointer';
window.addEventListener('mousedown', function(e) {
mouseDown = true;
document.body.style.cursor = 'ew-resize';
_cx = e.clientX;
stop();
});
window.addEventListener('mouseup', function(e) {
mouseDown = false;
document.body.style.cursor = 'pointer';
resume();
});
window.addEventListener('mousemove', function(e) {
if (mouseDown === true) {
var cx = e.clientX;
var dx = cx - _cx;
_cx = cx;
seek(dx);
}
});
// mobile
window.addEventListener('touchstart', function(e) {
_cx = e.touches[0].clientX;
stop();
e.preventDefault();
});
window.addEventListener('touchend', function(e) {
resume();
e.preventDefault();
});
window.addEventListener('touchmove', function(e) {
var cx = e.touches[0].clientX;
var dx = cx - _cx;
_cx = cx;
seek(dx);
e.preventDefault();
});
}
body {
margin: 0;
background: #fff;
}
canvas {
background: #fff;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/bas.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/175711/OrbitControls-2.js"></script>
<div id="three-container"></div>
Thanks in advance!

Categories

Resources