feat: optimize marble loading

This commit is contained in:
2025-12-27 17:42:53 +08:00
parent 59d5fe8c02
commit fa7ed7ec1b
2 changed files with 42 additions and 39 deletions

View File

@@ -84,49 +84,50 @@ export class MarbleFactory {
// Create marble (async image loading)
public async createMarble(entry: UserEntry): Promise<Marble> {
return new Promise((resolve, reject) => {
if (!entry?.id) {
reject(new Error("Invalid user entry"));
return;
}
if (!entry?.id) {
throw new Error("Invalid user entry");
}
const url = this.getAvatarUrl(entry.id);
const img = new Image();
const url = this.getAvatarUrl(entry.id);
const img = new Image();
img.src = url;
img.onload = () => {
const size = this.calculateMarbleSize();
const radius = size / 2;
const wrapper = this.createMarbleWrapper(entry, size, url);
const physics = this.generateRandomPhysics(radius);
try {
await img.decode();
} catch (e) {
throw new Error(`Failed to load image: ${url}`);
}
const { massScale, massOffset } = MARBLE_CONFIG.physics;
const marble: Marble = {
id: entry.id,
node: wrapper,
x: physics.x,
y: physics.y,
vx: physics.vx,
vy: physics.vy,
radius,
mass: radius * radius * massScale + massOffset,
};
const size = this.calculateMarbleSize();
const radius = size / 2;
const wrapper = this.createMarbleWrapper(entry, size, url);
const physics = this.generateRandomPhysics(radius);
this.container.appendChild(wrapper);
// Pre-set position to avoid flashing at 0,0
wrapper.style.transform = `translate(${physics.x - radius}px, ${physics.y - radius
}px)`;
// Fade in animation
setTimeout(() => {
wrapper.style.opacity = "1";
}, MARBLE_CONFIG.animation.fadeInDelay);
const { massScale, massOffset } = MARBLE_CONFIG.physics;
const marble: Marble = {
id: entry.id,
node: wrapper,
x: physics.x,
y: physics.y,
vx: physics.vx,
vy: physics.vy,
radius,
mass: radius * radius * massScale + massOffset,
};
resolve(marble);
};
this.container.appendChild(wrapper);
img.onerror = () => {
reject(new Error(`Failed to load image: ${url}`));
};
img.src = url;
// Fade in animation
requestAnimationFrame(() => {
wrapper.style.transition = "opacity 0.5s ease";
wrapper.style.opacity = "1";
});
return marble;
}
// Batch create marbles

View File

@@ -235,10 +235,12 @@ export class MarbleSystem {
}
// Batch add marbles
public async addMarbles(entries: UserEntry[]): Promise<Marble[]> {
const newMarbles = await this.factory.createMarbles(entries);
this.marbles.push(...newMarbles);
return newMarbles;
public addMarbles(entries: UserEntry[]): void {
entries.forEach((entry) => {
this.addMarble(entry).catch((err) => {
console.warn(`add marble for ${entry.id}:`, err);
});
});
}
// Remove marble