Add shared clear animation sizing and shatter support

This commit is contained in:
Daddy32
2025-12-16 20:37:12 +01:00
parent 065023abb4
commit 6c36abae0e
6 changed files with 94 additions and 6 deletions

View File

@@ -30,6 +30,7 @@
endScale: 1.8,
startOpacity: 0.95,
endOpacity: 0,
sizeScale: 1,
},
},
messages: { ...defaultMessageConfig },

View File

@@ -25,6 +25,14 @@
rope: true,
renderType: "line",
maxLengthMultiplier: 3.2,
clearAnimation: {
type: "pop",
durationMs: 220,
startScale: 0.9,
endScale: 4.4,
startOpacity: 0.66,
endOpacity: 0,
},
},
},
createBodies: (w, h) => {

View File

@@ -55,6 +55,11 @@
rope: true,
renderType: "line",
maxLengthMultiplier: 5.8,
clearAnimation: {
type: "shatter",
durationMs: 380,
sizeScale: 1.2,
},
},
},
createBodies: (w, h) => {

View File

@@ -36,6 +36,11 @@
rope: true,
renderType: "line",
maxLengthMultiplier: 2.5,
clearAnimation: {
type: "shatter",
durationMs: 380,
sizeScale: 2.2,
},
},
},
createBodies: (w, h) => {

View File

@@ -62,12 +62,13 @@
maxLinkLength: null, // optional absolute pixel cap for link reach
clearAnimation: {
// Visual “bling” when a chain clears. Defaults to a quick pop/fade.
type: "pop", // built-in default animation type
type: "pop", // built-in animation type ("pop" | "shatter")
durationMs: 320, // total animation time
startScale: 1, // initial scale multiplier
endScale: 1.8, // final scale multiplier
startOpacity: 0.95, // starting opacity
endOpacity: 0, // ending opacity
sizeScale: 1, // multiplies the base size (radius) used for the visual
render: null, // optional function({ targets, config, scene, ui }) to fully override rendering
},
},

View File

@@ -281,21 +281,20 @@
activeMessages.push(el);
};
const spawnClearEffects = (items = [], options = {}) => {
if (!clearFxEl || !Array.isArray(items) || items.length === 0) return;
const createPopEffect = (items, options) => {
const {
type = "pop",
durationMs = 320,
startScale = 1,
endScale = 1.8,
startOpacity = 0.95,
endOpacity = 0,
sizeScale = 1,
} = options;
items.forEach((item) => {
if (!item) return;
const size = Math.max(4, (item.radius || 18) * 2);
const size = Math.max(4, (item.radius || 18) * 2 * sizeScale);
const el = document.createElement("div");
el.className = `clear-effect clear-effect--${type}`;
el.className = "clear-effect clear-effect--pop";
el.style.width = `${size}px`;
el.style.height = `${size}px`;
el.style.left = `${item.x}px`;
@@ -314,6 +313,75 @@
});
};
const createShatterEffect = (items, options) => {
const {
durationMs = 360,
sizeScale = 1,
shardCount = 6,
startOpacity = 0.94,
} = options;
items.forEach((item) => {
if (!item) return;
const base = Math.max(8, (item.radius || 18) * sizeScale);
for (let i = 0; i < shardCount; i += 1) {
const piece = document.createElement("div");
piece.className = "clear-effect clear-effect--shatter";
piece.style.width = `${base * 0.6}px`;
piece.style.height = `${base * 0.6}px`;
piece.style.left = `${item.x}px`;
piece.style.top = `${item.y}px`;
piece.style.background = item.color || "#38bdf8";
piece.style.opacity = startOpacity;
piece.style.borderRadius = "14%";
piece.style.transform =
"translate(-50%, -50%) scale(0.9) rotate(0deg)";
clearFxEl.appendChild(piece);
const spread = base * 1.5;
const dx = (Math.random() - 0.5) * spread;
const dy = (Math.random() - 0.3) * spread;
const rot = (Math.random() - 0.5) * 260;
const scale = 0.9 + Math.random() * 0.5;
requestAnimationFrame(() => {
piece.style.transition = `transform ${durationMs}ms ease-out, opacity ${durationMs}ms ease-out`;
piece.style.transform = `translate(${dx}px, ${dy}px) scale(${scale}) rotate(${rot}deg)`;
piece.style.opacity = "0";
});
setTimeout(() => piece.remove(), durationMs + 80);
}
});
};
const spawnClearEffects = (items = [], options = {}) => {
if (!clearFxEl || !Array.isArray(items) || items.length === 0) return;
const {
type = "pop",
durationMs = 320,
startScale = 1,
endScale = 1.8,
startOpacity = 0.95,
endOpacity = 0,
sizeScale = 1,
shardCount = 6,
} = options;
if (type === "shatter") {
createShatterEffect(items, {
durationMs,
sizeScale,
shardCount,
startOpacity,
});
} else {
createPopEffect(items, {
durationMs,
startScale,
endScale,
startOpacity,
endOpacity,
sizeScale,
});
}
};
const api = {
sceneEl,
updateHud,