Fix vomit visibility, landing position, re-vomit guard, and animation jank

- Falling drop z-index 10002 (above companion) so it visibly exits mouth
- Landed puddle stays at z-index 9998 (below companion)
- Land position changed to 20px below Blobbi's container bottom, clamped
- Spawn position adjusted to config.size * 0.65 for mouth area
- Unified transform anchor to translate(-50%, -100%) in both states
- Keyframe ends at scale(1) opacity(1) to prevent pop on landing
- Reset vomitedThisCycle + peakIntensity in onDragStart during active
  cycle so each new qualifying drag can vomit independently
This commit is contained in:
filemon
2026-05-02 19:17:34 -03:00
parent abe12fdefa
commit 7ba94d72e4
4 changed files with 15 additions and 7 deletions
@@ -212,12 +212,12 @@ export function BlobbiCompanionLayer() {
// Compute spawn position (Blobbi's mouth area)
const spawnX = renderedPosition.x + config.size / 2;
const spawnY = renderedPosition.y + config.size * 0.55;
const spawnY = renderedPosition.y + config.size * 0.65;
// Land a short distance below Blobbi (near feet), not at viewport bottom
// Land about 20px below Blobbi's container bottom, clamped to viewport floor
const floorLimit = viewport.height - config.padding.bottom;
const landX = spawnX + (Math.random() * 30 - 15);
const landY = Math.min(renderedPosition.y + config.size * 0.9, floorLimit);
const landY = Math.min(renderedPosition.y + config.size + 20, floorLimit);
const newSplat: SplatData = {
id: vomitEvent.id,
@@ -50,13 +50,15 @@ export function VomitSplat({ id, spawnX, spawnY, landX, landY, onRemove }: Vomit
if (!landed) {
// Falling drop — animated from spawn to land position
// zIndex 10002 renders above companion (10000/10001) while falling
return (
<div
className="absolute pointer-events-none"
style={{
left: spawnX,
top: spawnY,
transform: 'translate(-50%, -50%)',
transform: 'translate(-50%, -100%)',
zIndex: 10002,
animation: `vomit-fall ${FALL_DURATION_MS}ms ease-in forwards`,
'--vomit-dx': `${fallDeltaX}px`,
'--vomit-dy': `${fallDeltaY}px`,
@@ -245,6 +245,13 @@ export function useShakeReaction({
if (ph.current === 'idle') {
toasted.current = false;
}
// Allow one vomit per drag: reset the per-drag guard when a new drag begins
// during an active cycle so the next release can qualify independently.
if (ph.current === 'dizzy' || ph.current === 'recovering' || ph.current === 'vomiting') {
vomitedThisCycle.current = false;
peakIntensity.current = 0;
}
}, []);
const onDragUpdate = useCallback(
+2 -3
View File
@@ -788,8 +788,7 @@
}
@keyframes vomit-fall {
0% { transform: translate(-50%, -50%) translate(0, 0) scale(1); opacity: 1; }
70% { opacity: 1; }
100% { transform: translate(-50%, -50%) translate(var(--vomit-dx), var(--vomit-dy)) scale(0.8); opacity: 0.8; }
0% { transform: translate(-50%, -100%) translate(0, 0) scale(1); opacity: 1; }
100% { transform: translate(-50%, -100%) translate(var(--vomit-dx), var(--vomit-dy)) scale(1); opacity: 1; }
}