Add shared clear animation sizing and shatter support
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
endScale: 1.8,
|
||||
startOpacity: 0.95,
|
||||
endOpacity: 0,
|
||||
sizeScale: 1,
|
||||
},
|
||||
},
|
||||
messages: { ...defaultMessageConfig },
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -55,6 +55,11 @@
|
||||
rope: true,
|
||||
renderType: "line",
|
||||
maxLengthMultiplier: 5.8,
|
||||
clearAnimation: {
|
||||
type: "shatter",
|
||||
durationMs: 380,
|
||||
sizeScale: 1.2,
|
||||
},
|
||||
},
|
||||
},
|
||||
createBodies: (w, h) => {
|
||||
|
||||
@@ -36,6 +36,11 @@
|
||||
rope: true,
|
||||
renderType: "line",
|
||||
maxLengthMultiplier: 2.5,
|
||||
clearAnimation: {
|
||||
type: "shatter",
|
||||
durationMs: 380,
|
||||
sizeScale: 2.2,
|
||||
},
|
||||
},
|
||||
},
|
||||
createBodies: (w, h) => {
|
||||
|
||||
@@ -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
|
||||
},
|
||||
},
|
||||
|
||||
78
src/ui.js
78
src/ui.js
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user