JSFiddle - React, Tailwind, and code Playground
by Scott Kaye
HTML
<img src="https://i.imgur.com/GoWC74k.jpg" class="bubbles" />
CSS
html,
body {
margin: 0;
padding: 0;
overflow: hidden;
}
img {
max-width: 100vw;
max-height: 100vh;
}
JavaScript
function addBubblesOverlay(element) {
const rect = element.getBoundingClientRect();
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(rect.width / -2, rect.width / 2, rect.height / 2, rect.height / -2, 0, 1000);
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setSize(rect.width, rect.height);
const wrapperElement = document.createElement("div");
element.parentNode.insertBefore(wrapperElement, element);
wrapperElement.appendChild(renderer.domElement);
wrapperElement.appendChild(element);
renderer.domElement.style.position = "absolute";
renderer.domElement.style.mixBlendMode = "overlay";
renderer.domElement.style.opacity = 0.5;
function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
// Create geometries
const geometries = new Array(5)
.fill(0)
.map(() => new THREE.CircleGeometry(rand(rect.width / 20, rect.width / 10), 32));
const meshWrappers = new Array(30)
.fill(0)
.map(() => {
const geometry = geometries[(Math.random() * geometries.length) | 0];
const material = new THREE.MeshBasicMaterial({
color: 0xffffff,
transparent: true
});
return {
isAppearing: false,
vx: 0,
vy: 0,
mesh: new THREE.Mesh(geometry, material)
};
});
function initMeshWrapper(wrapper) {
wrapper.mesh.position.x = (window.innerWidth / -2) + Math.random() * window.innerWidth;
wrapper.mesh.position.y = (window.innerHeight / -2) + Math.random() * window.innerHeight;
wrapper.mesh.material.opacity = 0;
wrapper.vx = (Math.random() - 0.5) / 2;
wrapper.vy = (Math.random() - 0.5) / 2;
wrapper.isAppearing = true;
}
// Initialize scene
meshWrappers.forEach(wrapper => {
initMeshWrapper(wrapper);
// Special case for the first run
wrapper.mesh.material.opacity = Math.random();
wrapper.isAppearing = false;
...