xxxxxxxxxx
275
let zodiacMessages = {
"Aries": "Fearless Fire",
"Taurus": "Steady as the Stars",
"Gemini": "Duality of Light",
"Cancer": "Moon’s Embrace",
"Leo": "Born from Stardust",
"Virgo": "Cosmic Precision",
"Libra": "Balancing the Galaxy",
"Scorpio": "Celestial Depths",
"Sagittarius": "Infinity Seeker",
"Capricorn": "Climbing the Cosmos",
"Aquarius": "Waves of the Universe",
"Pisces": "Dreamer of Nebulae"
};
let bubbles = [];
let fortunes = [];
let rainingLetters = [];
let stars = [];
function setup() {
createCanvas(400, 400);
// Create twinkling stars
for (let i = 0; i < 100; i++) {
stars.push({
x: random(width),
y: random(height),
baseBrightness: random(150, 200),
twinkleOffset: random(360),
twinkleSpeed: random(0.005, 0.02),
});
}
}
function draw() {
drawBackground();
// Update and display fortunes
for (let i = fortunes.length - 1; i >= 0; i--) {
fortunes[i].update();
fortunes[i].display();
}
// Update and display moving bubbles
for (let i = bubbles.length - 1; i >= 0; i--) {
bubbles[i].update();
bubbles[i].display();
}
// Update and display falling letters
for (let i = rainingLetters.length - 1; i >= 0; i--) {
rainingLetters[i].update();
rainingLetters[i].display();
}
// Check for collisions between bubbles
for (let i = 0; i < bubbles.length; i++) {
for (let j = i + 1; j < bubbles.length; j++) {
let bubble1 = bubbles[i];
let bubble2 = bubbles[j];
let distance = dist(bubble1.x, bubble1.y, bubble2.x, bubble2.y);
let minDistance = (bubble1.radius / 2) + (bubble2.radius / 2);
if (distance < minDistance) {
bubble1.bounce(bubble2);
}
}
}
}
function mousePressed() {
for (let i = bubbles.length - 1; i >= 0; i--) {
let bubble = bubbles[i];
let distance = dist(mouseX, mouseY, bubble.x, bubble.y);
if (distance < bubble.radius / 2) {
bubble.pop();
return;
}
}
if (!isOverlapping(mouseX, mouseY)) {
let sign = random(Object.keys(zodiacMessages));
bubbles.push(new Bubble(mouseX, mouseY, sign));
}
}
function isOverlapping(x, y) {
for (let bubble of bubbles) {
let distance = dist(x, y, bubble.x, bubble.y);
if (distance < bubble.radius) {
return true;
}
}
return false;
}
class Bubble {
constructor(x, y, sign) {
this.x = x;
this.y = y;
this.radius = random(50, 70);
this.sign = sign;
this.xSpeed = random(0.5, 1);
this.ySpeed = random(0.5, 1);
this.color1 = color(random(180, 255), random(180, 255), 255, 150);
this.color2 = color(random(200, 255), random(180, 200), 255, 80);
this.glow = color(255, 255, 255, 120);
}
update() {
this.x += this.xSpeed;
this.y += this.ySpeed;
// Bounce off walls
if (this.x < this.radius / 2 || this.x > width - this.radius / 2) {
this.xSpeed *= -1;
}
if (this.y < this.radius / 2 || this.y > height - this.radius / 2) {
this.ySpeed *= -1;
}
}
display() {
push();
translate(this.x, this.y);
drawingContext.shadowBlur = 15;
drawingContext.shadowColor = this.glow;
let interColor = lerpColor(this.color1, this.color2, 0.5);
fill(interColor);
noStroke();
ellipse(0, 0, this.radius);
// Dynamically adjust text size to fit inside the bubble
let textSizeToUse = this.radius / 3;
textSize(textSizeToUse);
// Reduce text size if it's too wide for the bubble
while (textWidth(this.sign) > this.radius * 0.7 && textSizeToUse > 10) {
textSizeToUse -= 1;
}
fill(255);
textSize(textSizeToUse);
textAlign(CENTER, CENTER);
text(this.sign, 0, 0);
pop();
}
pop() {
let message = zodiacMessages[this.sign];
fortunes.push(new Fortune(this.x, this.y, message));
bubbles.splice(bubbles.indexOf(this), 1);
}
bounce(otherBubble) {
let overlap = (this.radius / 2 + otherBubble.radius / 2) - dist(this.x, this.y, otherBubble.x, otherBubble.y);
let angle = atan2(this.y - otherBubble.y, this.x - otherBubble.x);
let overlapX = cos(angle) * overlap;
let overlapY = sin(angle) * overlap;
this.x += overlapX / 2;
this.y += overlapY / 2;
otherBubble.x -= overlapX / 2;
otherBubble.y -= overlapY / 2;
let damping = 0.9;
let tempXSpeed = this.xSpeed;
let tempYSpeed = this.ySpeed;
this.xSpeed = otherBubble.xSpeed * damping;
this.ySpeed = otherBubble.ySpeed * damping;
otherBubble.xSpeed = tempXSpeed * damping;
otherBubble.ySpeed = tempYSpeed * damping;
}
}
class Fortune {
constructor(x, y, message) {
this.x = x;
this.y = y;
this.message = message;
this.alpha = 0; // Start invisible for fade-in effect
this.timer = 90; // Now lasts for 3 seconds
this.fadeInSpeed = 5; // Controls how fast it fades in
this.released = false;
this.floatOffset = 0; // For a slight floating effect
}
update() {
if (this.timer > 0) {
this.timer--;
if (this.alpha < 255) this.alpha += this.fadeInSpeed; // Gradually appear
this.floatOffset = sin(frameCount * 0.1) * 2; // Subtle floating effect
} else if (!this.released) {
this.releaseLetters();
this.released = true;
}
}
display() {
if (this.timer > 0) {
push();
translate(this.x, this.y + this.floatOffset);
// Glowing text effect
fill(255, 255, 255, this.alpha);
textSize(14);
textAlign(CENTER, CENTER);
drawingContext.shadowBlur = 10;
drawingContext.shadowColor = color(255, 200, 255, this.alpha);
text(this.message, 0, 0);
pop();
}
}
releaseLetters() {
for (let i = 0; i < this.message.length; i++) {
rainingLetters.push(new Letter(this.x, this.y, this.message.charAt(i)));
}
}
}
class Letter {
constructor(x, y, char) {
this.x = x + random(-10, 10);
this.y = y;
this.char = char;
this.speed = random(1, 3);
this.alpha = 255;
}
update() {
this.y += this.speed;
this.alpha -= 3;
}
display() {
push();
translate(this.x, this.y);
fill(255, this.alpha);
textSize(20);
textAlign(CENTER, CENTER);
text(this.char, 0, 0);
pop();
}
}
function drawBackground() {
for (let y = 0; y < height; y++) {
let inter = map(y, 0, height, 0, 1);
let c = lerpColor(color(30, 0, 60), color(100, 0, 150), inter);
stroke(c);
line(0, y, width, y);
}
noStroke();
for (let star of stars) {
let brightness = star.baseBrightness + sin(frameCount * star.twinkleSpeed + star.twinkleOffset) * 40;
brightness = constrain(brightness, 0, 255);
fill(255, 255, 255, brightness);
ellipse(star.x, star.y, random(1, 3));
}
}