Make the pee/poo confetti a bottom-up fireworks fountain
Rework the effect: instead of a small burst from the tapped button, 30 emoji launch from across the bottom of the screen, rise to random peaks while drifting sideways, then arc back down and fade — staggered launches and varied speed/size/rotation for a fuller, firework feel. Still pointer-events:none, self-removing, and gated by the Settings toggle and reduced-motion.
This commit is contained in:
+17
-17
@@ -3177,10 +3177,10 @@
|
||||
snackbarTimer = setTimeout(hideSnackbar, 5000);
|
||||
}
|
||||
|
||||
function quickLog(type, originEl) {
|
||||
function quickLog(type) {
|
||||
const ev = addEvent(type, "", Date.now());
|
||||
showSnackbar(`${EVENT_LABELS[type]} logged`, ev);
|
||||
if (type === "pee" || type === "poo") pottyConfetti(type, originEl);
|
||||
if (type === "pee" || type === "poo") pottyConfetti(type);
|
||||
}
|
||||
|
||||
// A little burst of 💧/💩 from the tapped button when a pee/poo is logged.
|
||||
@@ -3203,28 +3203,28 @@
|
||||
}
|
||||
return confettiLayerEl;
|
||||
}
|
||||
function pottyConfetti(type, originEl) {
|
||||
function pottyConfetti(type) {
|
||||
if (!confettiEnabled()) return;
|
||||
if (window.matchMedia && matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
||||
const emoji = type === "poo" ? "💩" : "💧";
|
||||
const layer = confettiLayer();
|
||||
const r = originEl && originEl.getBoundingClientRect
|
||||
? originEl.getBoundingClientRect()
|
||||
: { left: innerWidth / 2, top: innerHeight / 2, width: 0, height: 0 };
|
||||
const ox = r.left + r.width / 2, oy = r.top + r.height / 2;
|
||||
for (let i = 0; i < 16; i++) {
|
||||
const W = window.innerWidth, H = window.innerHeight;
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const piece = document.createElement("span");
|
||||
piece.className = "confetti-piece";
|
||||
piece.textContent = emoji;
|
||||
const ang = Math.random() * Math.PI * 2;
|
||||
const dist = 60 + Math.random() * 130;
|
||||
piece.style.left = `${ox}px`;
|
||||
piece.style.top = `${oy}px`;
|
||||
piece.style.setProperty("--dx", `${Math.round(Math.cos(ang) * dist)}px`);
|
||||
piece.style.setProperty("--dy", `${Math.round(Math.sin(ang) * dist - 50)}px`); // bias upward
|
||||
// Launch from a point across the bottom, shoot up to a random peak, drift
|
||||
// sideways, then arc back down (endY is below the peak) while fading.
|
||||
const peakY = -Math.round(H * (0.45 + Math.random() * 0.45));
|
||||
piece.style.left = `${Math.round(W * (0.1 + Math.random() * 0.8))}px`;
|
||||
piece.style.top = `${H + 20}px`;
|
||||
piece.style.setProperty("--peakY", `${peakY}px`);
|
||||
piece.style.setProperty("--dx", `${Math.round((Math.random() * 2 - 1) * W * 0.22)}px`);
|
||||
piece.style.setProperty("--endY", `${peakY + Math.round(H * 0.35)}px`);
|
||||
piece.style.setProperty("--rot", `${Math.round(Math.random() * 720 - 360)}deg`);
|
||||
piece.style.fontSize = `${Math.round(14 + Math.random() * 16)}px`;
|
||||
piece.style.animationDuration = `${Math.round(900 + Math.random() * 600)}ms`;
|
||||
piece.style.fontSize = `${Math.round(16 + Math.random() * 18)}px`;
|
||||
piece.style.animationDuration = `${Math.round(1100 + Math.random() * 700)}ms`;
|
||||
piece.style.animationDelay = `${Math.round(Math.random() * 280)}ms`;
|
||||
piece.addEventListener("animationend", () => piece.remove());
|
||||
layer.appendChild(piece);
|
||||
}
|
||||
@@ -3289,7 +3289,7 @@
|
||||
// Weigh-ins need a typed value and meals ask for grams, so those two
|
||||
// keep the full dialog.
|
||||
if (type === "weight" || type === "eat") { openNoteDialog(type); return; }
|
||||
quickLog(type, btn);
|
||||
quickLog(type);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
[
|
||||
{ "date": "2026-08-01", "text": "Logging a pee or poo now sets off a little burst of 💧/💩 confetti from the button — a tiny celebration you can switch off in Settings (and it honours a reduced-motion preference)" },
|
||||
{ "date": "2026-08-01", "text": "Logging a pee or poo now sets off 💧/💩 fireworks that shoot up from the bottom of the screen — a little celebration you can switch off in Settings (and it honours a reduced-motion preference)" },
|
||||
{ "date": "2026-08-01", "text": "Tidied the header on long names and ages — the name now truncates instead of shoving the buttons, and the age reads as a compact \"16 wk · 3 mo 3 wk\"; weight-log rows are a single line again (\"Aug 1 · 16 wk\")" },
|
||||
{ "date": "2026-07-26", "text": "Added a fan-chart view of the pedigree (toggle it in the header): your dog at the centre with each generation fanning outward as a ring, so many generations fit at once without the tree sprawling sideways — tap a wedge for that dog, and repeated ancestors keep their colour" },
|
||||
{ "date": "2026-07-26", "text": "Added a Collapse all / Expand all toggle to the pedigree, to fold the whole tree down to your dog or open every branch at once" },
|
||||
|
||||
+12
-11
@@ -1361,18 +1361,19 @@ section.collapsed > :not(h2) { display: none; }
|
||||
position: absolute;
|
||||
line-height: 1;
|
||||
will-change: transform, opacity;
|
||||
animation-name: potty-burst;
|
||||
animation-timing-function: cubic-bezier(0.2, 0.6, 0.35, 1);
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes potty-burst {
|
||||
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.4) rotate(0deg); }
|
||||
12% { opacity: 1; }
|
||||
100% {
|
||||
opacity: 0;
|
||||
/* fly out to (dx, dy) then keep falling (gravity) */
|
||||
transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy) + 150px)) scale(1) rotate(var(--rot));
|
||||
animation-name: potty-firework;
|
||||
animation-timing-function: ease-out;
|
||||
/* `both` so the 0% state (invisible, at the bottom) also applies during the
|
||||
per-piece launch delay — no flash before it takes off. */
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
/* Launch up from the bottom, rise to a peak (spreading sideways), then arc back
|
||||
down while fading — a firework fountain. Distances come from JS custom props. */
|
||||
@keyframes potty-firework {
|
||||
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.5) rotate(0deg); }
|
||||
8% { opacity: 1; }
|
||||
60% { opacity: 1; transform: translate(calc(-50% + var(--dx) * 0.7), calc(-50% + var(--peakY))) scale(1) rotate(calc(var(--rot) * 0.7)); }
|
||||
100% { opacity: 0; transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--endY))) scale(0.9) rotate(var(--rot)); }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.confetti-piece { display: none; }
|
||||
|
||||
Reference in New Issue
Block a user