.bubble-link {
position: absolute;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
cursor: pointer;
transition: transform 0.3s ease, box-shadow 0.3s ease;
background: transparent;
color: white;
font-weight: bold;
text-decoration: none;
box-shadow: none;
z-index: 10;
}
.bubble-link:hover {
transform: scale(1.05);
z-index: 100;
}
.bubble-xlarge {
width: 280px;
height: 280px;
font-size: 26px;
}
.bubble-large {
width: 220px;
height: 220px;
font-size: 22px;
}
.bubble-medium {
width: 170px;
height: 170px;
font-size: 18px;
}
.bubble-small {
width: 130px;
height: 130px;
font-size: 16px;
}
@media (max-width: 768px) {
#bubbles-section {
height: 500px;
}
.bubble-xlarge {
width: 160px;
height: 160px;
font-size: 18px;
}
.bubble-large {
width: 130px;
height: 130px;
font-size: 16px;
}
.bubble-medium {
width: 110px;
height: 110px;
font-size: 14px;
}
.bubble-small {
width: 90px;
height: 90px;
font-size: 12px;
}
}
(function() {
// Configuration - MODIFIE TES LIENS ICI
const bubblesConfig = [
{ text: "Graphisme", url: "/graphisme", size: "xlarge" },
{ text: "Produit", url: "/produit", size: "large" },
{ text: "Animation", url: "/animation", size: "medium" },
{ text: "Dynamisation", url: "/dynamisation", size: "small" },
{ text: "Programmation", url: "/programmation", size: "medium" }
];
const canvas = document.getElementById('bubbles-canvas');
const ctx = canvas.getContext('2d');
const container = document.getElementById('bubbles-container');
function resizeCanvas() {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
}
resizeCanvas();
class Bubble {
constructor(config, index) {
this.text = config.text;
this.url = config.url;
this.size = config.size;
// Tailles différentes selon la config
const sizeMap = {
'xlarge': 170,
'large': 110,
'medium': 85,
'small': 65
};
this.radius = sizeMap[config.size] || 85;
// Position initiale aléatoire partout sur l'écran
const margin = 150;
this.x = margin + Math.random() * (canvas.width - margin * 2);
this.y = margin + Math.random() * (canvas.height - margin * 2);
this.originX = this.x;
this.originY = this.y;
this.phase = Math.random() * Math.PI * 2;
this.time = 0;
// Vitesse aléatoire pour chaque bulle
this.speedX = (Math.random() - 0.5) * 0.4;
this.speedY = (Math.random() - 0.5) * 0.4;
this.element = this.createElement();
}
createElement() {
const div = document.createElement('a');
div.className = `bubble-link bubble-${this.size}`;
div.href = this.url;
div.textContent = this.text;
container.appendChild(div);
return div;
}
update() {
this.time += 0.01;
// Mouvement libre avec vitesse constante
this.x += this.speedX;
this.y += this.speedY;
// Rebond sur les bords avec marge
const margin = this.radius + 20;
if (this.x canvas.width - margin) {
this.speedX *= -1;
this.x = Math.max(margin, Math.min(this.x, canvas.width - margin));
}
if (this.y canvas.height - margin) {
this.speedY *= -1;
this.y = Math.max(margin, Math.min(this.y, canvas.height - margin));
}
// Petit mouvement organique additionnel
const organicX = Math.sin(this.time + this.phase) * 3;
const organicY = Math.cos(this.time * 0.8 + this.phase) * 3;
this.element.style.left = (this.x + organicX - this.element.offsetWidth / 2) + 'px';
this.element.style.top = (this.y + organicY - this.element.offsetHeight / 2) + 'px';
}
checkCollision(other) {
const dx = this.x - other.x;
const dy = this.y - other.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const minDistance = this.radius + other.radius;
if (distance
new Bubble(config, index)
);
function drawMetaballs() {
const imageData = ctx.createImageData(canvas.width, canvas.height);
const data = imageData.data;
const step = 1;
for (let y = 0; y < canvas.height; y += step) {
for (let x = 0; x 0) {
// Réduction du rayon pour effet de fusion plus serré
const effectiveRadius = bubble.radius * 0.9;
sum += (effectiveRadius * effectiveRadius) / (dist * dist);
}
}
// Seuil augmenté pour réduire la zone de fusion
if (sum > 1.1) {
for (let dy = 0; dy < step; dy++) {
for (let dx = 0; dx < step; dx++) {
const idx = ((y + dy) * canvas.width + (x + dx)) * 4;
if (idx
bubble.update());
// Vérifier les collisions entre toutes les bulles
for (let i = 0; i < bubbleObjects.length; i++) {
for (let j = i + 1; j {
const oldWidth = canvas.width;
const oldHeight = canvas.height;
resizeCanvas();
// Ajuster les positions proportionnellement
bubbleObjects.forEach((bubble) => {
bubble.x = (bubble.x / oldWidth) * canvas.width;
bubble.y = (bubble.y / oldHeight) * canvas.height;
bubble.originX = bubble.x;
bubble.originY = bubble.y;
});
});
animate();
})();