Háttéranimációk
Konstellációs háló
Élő előnézet – az animáció teljes méretben.
Élő előnézet
HTML · CSS · JS
<div class="we-bg-constellation">
<canvas class="we-bg-constellation-canvas"></canvas>
<div class="we-bg-constellation-content">
<span>WEBELEMENTS</span>
<h2>Konstelláció</h2>
<p>
Egy élő hálózat, amely folyamatosan változik a háttérben.
</p>
</div>
</div>
.we-bg-constellation {
position: relative;
width: 100%;
min-height: 520px;
overflow: hidden;
background:
radial-gradient(
circle at 50% 45%,
rgba(63, 159, 99, .08),
transparent 42%
),
#080b11;
display: flex;
align-items: center;
justify-content: center;
isolation: isolate;
}
.we-bg-constellation-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
pointer-events: none;
z-index: 1;
}
.we-bg-constellation-content {
position: relative;
z-index: 3;
width: min(720px, 90%);
padding: 48px 44px;
text-align: center;
pointer-events: none;
}
.we-bg-constellation-content span {
display: block;
margin-bottom: 16px;
color: #7ac98e;
font-family: Inter, system-ui, sans-serif;
font-size: 10px;
font-weight: 700;
letter-spacing: .24em;
}
.we-bg-constellation-content h2 {
margin: 0 0 18px;
color: #f4f1e9;
font-family: "Bodoni Moda", Georgia, serif;
font-size: clamp(42px, 7vw, 78px);
font-weight: 500;
line-height: 1;
letter-spacing: -.02em;
}
.we-bg-constellation-content p {
max-width: 560px;
margin: 0 auto;
color: rgba(244, 241, 233, .62);
font-family: Inter, system-ui, sans-serif;
font-size: 14px;
line-height: 1.8;
}
@media (max-width: 700px) {
.we-bg-constellation {
min-height: 460px;
}
.we-bg-constellation-content {
padding: 35px 22px;
}
.we-bg-constellation-content h2 {
font-size: clamp(38px, 12vw, 58px);
}
}
document.querySelectorAll(".we-bg-constellation").forEach(container => {
const canvas = container.querySelector(
".we-bg-constellation-canvas"
);
const ctx = canvas.getContext("2d");
const settings = {
desktop: 115,
mobile: 52,
maxDistance: 155,
mouseRadius: 230,
minSpeed: 0.12,
maxSpeed: 0.55,
mouseForce: 0.035
};
let particles = [];
let width = 0;
let height = 0;
let dpr = 1;
const pointer = {
x: null,
y: null,
active: false
};
function resizeCanvas() {
const rect = container.getBoundingClientRect();
width = rect.width;
height = rect.height;
dpr = Math.min(window.devicePixelRatio || 1, 2);
canvas.width = width * dpr;
canvas.height = height * dpr;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
createParticles();
}
function createParticles() {
const amount =
window.innerWidth <= 700
? settings.mobile
: settings.desktop;
particles = [];
for (let i = 0; i < amount; i++) {
const angle = Math.random() * Math.PI * 2;
const speed =
settings.minSpeed +
Math.random() *
(settings.maxSpeed - settings.minSpeed);
particles.push({
x: Math.random() * width,
y: Math.random() * height,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
radius: Math.random() * 1.35 + .65,
phase: Math.random() * Math.PI * 2,
waveX: Math.random() * Math.PI * 2,
waveY: Math.random() * Math.PI * 2,
waveSpeedX:
.0008 + Math.random() * .0014,
waveSpeedY:
.0007 + Math.random() * .0013,
waveAmountX:
.12 + Math.random() * .34,
waveAmountY:
.12 + Math.random() * .34
});
}
}
function updateParticle(particle, time) {
particle.waveX += particle.waveSpeedX;
particle.waveY += particle.waveSpeedY;
particle.x +=
particle.vx +
Math.sin(particle.waveX + particle.phase) *
particle.waveAmountX;
particle.y +=
particle.vy +
Math.cos(particle.waveY + particle.phase) *
particle.waveAmountY;
if (
pointer.active &&
pointer.x !== null &&
pointer.y !== null
) {
const dx = particle.x - pointer.x;
const dy = particle.y - pointer.y;
const distance = Math.sqrt(
dx * dx + dy * dy
);
if (
distance > 0 &&
distance < settings.mouseRadius
) {
const force =
(1 - distance / settings.mouseRadius) *
settings.mouseForce;
particle.vx +=
(dx / distance) * force;
particle.vy +=
(dy / distance) * force;
}
}
const velocity = Math.sqrt(
particle.vx * particle.vx +
particle.vy * particle.vy
);
if (velocity > settings.maxSpeed) {
particle.vx =
(particle.vx / velocity) *
settings.maxSpeed;
particle.vy =
(particle.vy / velocity) *
settings.maxSpeed;
}
if (velocity < settings.minSpeed) {
const safeVelocity =
velocity || settings.minSpeed;
particle.vx =
(particle.vx / safeVelocity) *
settings.minSpeed;
particle.vy =
(particle.vy / safeVelocity) *
settings.minSpeed;
}
const margin = 30;
if (particle.x < -margin) {
particle.x = width + margin;
}
if (particle.x > width + margin) {
particle.x = -margin;
}
if (particle.y < -margin) {
particle.y = height + margin;
}
if (particle.y > height + margin) {
particle.y = -margin;
}
}
function drawConnections() {
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const first = particles[i];
const second = particles[j];
const dx = first.x - second.x;
const dy = first.y - second.y;
const distance =
Math.sqrt(dx * dx + dy * dy);
if (distance > settings.maxDistance) {
continue;
}
let opacity =
1 - distance / settings.maxDistance;
opacity *= .42;
let lineColor =
`rgba(95, 189, 125, ${opacity})`;
let lineWidth = .65;
if (
pointer.active &&
pointer.x !== null &&
pointer.y !== null
) {
const firstMouseDistance =
Math.hypot(
first.x - pointer.x,
first.y - pointer.y
);
const secondMouseDistance =
Math.hypot(
second.x - pointer.x,
second.y - pointer.y
);
if (
firstMouseDistance < settings.mouseRadius ||
secondMouseDistance < settings.mouseRadius
) {
opacity =
Math.min(
.82,
opacity + .24
);
lineColor =
`rgba(122, 201, 142, ${opacity})`;
lineWidth = .9;
}
}
ctx.beginPath();
ctx.moveTo(first.x, first.y);
ctx.lineTo(second.x, second.y);
ctx.strokeStyle = lineColor;
ctx.lineWidth = lineWidth;
ctx.stroke();
}
}
}
function drawPointerConnections() {
if (
!pointer.active ||
pointer.x === null ||
pointer.y === null
) {
return;
}
for (const particle of particles) {
const dx = particle.x - pointer.x;
const dy = particle.y - pointer.y;
const distance =
Math.sqrt(dx * dx + dy * dy);
if (distance > settings.mouseRadius) {
continue;
}
const opacity =
(1 - distance / settings.mouseRadius) *
.62;
ctx.beginPath();
ctx.moveTo(pointer.x, pointer.y);
ctx.lineTo(particle.x, particle.y);
ctx.strokeStyle =
`rgba(122, 201, 142, ${opacity})`;
ctx.lineWidth = .85;
ctx.stroke();
}
const glow = ctx.createRadialGradient(
pointer.x,
pointer.y,
0,
pointer.x,
pointer.y,
90
);
glow.addColorStop(
0,
"rgba(95, 189, 125, .10)"
);
glow.addColorStop(
1,
"rgba(95, 189, 125, 0)"
);
ctx.beginPath();
ctx.arc(
pointer.x,
pointer.y,
90,
0,
Math.PI * 2
);
ctx.fillStyle = glow;
ctx.fill();
}
function drawParticles() {
for (const particle of particles) {
const glow = ctx.createRadialGradient(
particle.x,
particle.y,
0,
particle.x,
particle.y,
7
);
glow.addColorStop(
0,
"rgba(244, 241, 233, .78)"
);
glow.addColorStop(
.22,
"rgba(122, 201, 142, .32)"
);
glow.addColorStop(
1,
"rgba(122, 201, 142, 0)"
);
ctx.beginPath();
ctx.arc(
particle.x,
particle.y,
7,
0,
Math.PI * 2
);
ctx.fillStyle = glow;
ctx.fill();
ctx.beginPath();
ctx.arc(
particle.x,
particle.y,
particle.radius,
0,
Math.PI * 2
);
ctx.fillStyle =
"rgba(244, 241, 233, .72)";
ctx.fill();
}
}
function render(time) {
ctx.clearRect(
0,
0,
width,
height
);
for (const particle of particles) {
updateParticle(particle, time);
}
drawConnections();
drawPointerConnections();
drawParticles();
requestAnimationFrame(render);
}
container.addEventListener(
"mousemove",
event => {
const rect =
container.getBoundingClientRect();
pointer.x =
event.clientX - rect.left;
pointer.y =
event.clientY - rect.top;
pointer.active = true;
}
);
container.addEventListener(
"mouseleave",
() => {
pointer.x = null;
pointer.y = null;
pointer.active = false;
}
);
window.addEventListener(
"resize",
resizeCanvas
);
resizeCanvas();
requestAnimationFrame(render);
});