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:
@@ -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
@@ -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; }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user