Ce code HTML/CSS/JS affiche un texte centré sur la page, survolé par 7 ballons colorés animés qui montent et oscillent doucement, créant un effet visuel léger et festif.
<div id="balloons-hero">
<div class="hero-text">Bienvenue à notre événement</div>
<div class="sky" aria-hidden="true"></div>
</div>
<style>
#balloons-hero {
position: relative;
height: 70vh;
min-height: 380px;
overflow: hidden;
background: #fff;
}
#balloons-hero .hero-text {
position: absolute;
inset: 0;
display: grid;
place-items: center;
text-align: center;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
font-weight: 700;
font-size: clamp(28px, 5vw, 64px);
line-height: 1.1;
color: #111827;
z-index: 1;
}
#balloons-hero .sky {
position: absolute;
inset: 0;
z-index: 2;
pointer-events: none;
}
.balloon {
position: absolute;
will-change: transform, opacity;
transform: translate(-50%, 0);
width: var(--size, 72px);
height: calc(var(--size, 72px) * 1.25);
border-radius: 50% 50% 48% 48%;
/* Plus opaque qu'avant */
--alpha: 0.85;
background:
radial-gradient(circle at 28% 28%, rgba(255, 255, 255, .75) 0 18%, transparent 22%),
rgba(var(--rgb, 239, 68, 68), var(--alpha));
box-shadow: 0 8px 18px rgba(0, 0, 0, .12);
}
/* Ficelle courbée en SVG */
.balloon svg {
position: absolute;
top: 95%;
left: 50%;
transform-origin: top center;
transform: translateX(-50%) rotate(var(--tilt, 0deg));
}
.balloon path {
fill: none;
stroke: #9ca3af;
stroke-width: 2;
opacity: 0.9;
}
.balloon::before {
content: "";
position: absolute;
bottom: -10%;
left: 50%;
width: 60%;
height: 8%;
transform: translateX(-50%);
filter: blur(4px);
background: rgba(0, 0, 0, .08);
border-radius: 50%;
}
@media (prefers-reduced-motion: reduce) {
.balloon { transition: none; }
}
</style>
<script>
(function () {
const sky = document.querySelector("#balloons-hero .sky");
const W = () => sky.clientWidth;
const H = () => sky.clientHeight;
const colors = [
[239, 68, 68], // rouge
[37, 99, 235], // bleu
[250, 204, 21], // jaune
[34, 197, 94], // vert
[239, 68, 68],
[37, 99, 235],
[34, 197, 94]
];
const COUNT = 7;
function rand(min, max) { return Math.random() * (max - min) + min; }
function clamp(x, a, b) { return Math.min(b, Math.max(a, x)); }
const balloons = [];
for (let i = 0; i < COUNT; i++) {
const el = document.createElement("div");
el.className = "balloon";
const size = Math.round(rand(58, 86));
const rgb = colors[i % colors.length];
el.style.setProperty("--size", size + "px");
el.style.setProperty("--rgb", rgb.join(","));
// Ajout ficelle courbée via SVG
const svgNS = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(svgNS, "svg");
svg.setAttribute("width", "2");
svg.setAttribute("height", size * 0.9);
svg.setAttribute("viewBox", "0 0 2 " + (size * 0.9));
const path = document.createElementNS(svgNS, "path");
path.setAttribute("d", `M1 0 Q ${rand(-4,4)} ${size*0.3} , 1 ${size*0.6} T 1 ${size*0.9}`);
svg.appendChild(path);
el.appendChild(svg);
sky.appendChild(el);
const b = {
el,
path,
size,
baseX: rand(0.08, 0.92),
amp: rand(0.04, 0.09),
omega: rand(0.4, 0.85),
vy: rand(18, 32),
phase: rand(0, Math.PI * 2),
y: rand(H() * 0.15, H() * 0.95),
t0: performance.now() / 1000 - rand(0, 10)
};
balloons.push(b);
}
function tick(nowMs) {
const t = nowMs / 1000;
const w = W(), h = H();
for (const b of balloons) {
const x = (b.baseX + b.amp * Math.sin(b.omega * (t - b.t0) + b.phase)) * w;
b.y -= b.vy * (1 / 60);
if (b.y < -b.size * 1.6) {
b.y = h + rand(10, 120);
b.baseX = rand(0.08, 0.92);
b.amp = rand(0.04, 0.09);
b.omega = rand(0.4, 0.85);
b.vy = rand(18, 32);
b.phase = rand(0, Math.PI * 2);
}
const vx = b.amp * b.omega * Math.cos(b.omega * (t - b.t0) + b.phase) * w;
const tilt = clamp(vx * 0.06, -14, 14);
b.el.style.setProperty("--tilt", tilt.toFixed(2) + "deg");
const alpha = 0.75 + (1 - b.y / h) * 0.15;
b.el.style.setProperty("--alpha", clamp(alpha, 0.75, 0.9).toFixed(2));
// Mise à jour dynamique de la courbure de la ficelle
b.path.setAttribute("d", `M1 0 Q ${clamp(vx*0.08,-6,6)} ${b.size*0.3} , 1 ${b.size*0.6} T 1 ${b.size*0.9}`);
b.el.style.transform = `translate(${x}px, ${b.y}px)`;
}
requestAnimationFrame(tick);
}
(function initStartPositions() {
const w = W();
for (const b of balloons) {
const x = (b.baseX + b.amp * Math.sin(b.phase)) * w;
b.el.style.transform = `translate(${x}px, ${b.y}px)`;
}
})();
requestAnimationFrame(tick);
})();
</script>