I'm never making an animation again but I'm dying laughing
This commit is contained in:
@@ -1198,6 +1198,64 @@ article.card[data-bookmarked="true"] {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes cardFlipFall {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
transform:
|
||||||
|
perspective(1400px) rotateX(0) rotateY(0) rotateZ(0)
|
||||||
|
translate3d(0, 0, 0) scale(1);
|
||||||
|
filter: brightness(1) saturate(1);
|
||||||
|
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
18% {
|
||||||
|
opacity: 1;
|
||||||
|
transform:
|
||||||
|
perspective(1400px) rotateX(6deg) rotateY(-12deg) rotateZ(-2deg)
|
||||||
|
translate3d(-4px, -16px, 40px) scale(1.04);
|
||||||
|
filter: brightness(1.18) saturate(1.15);
|
||||||
|
box-shadow:
|
||||||
|
0 30px 60px rgba(0, 0, 0, 0.55),
|
||||||
|
0 0 32px rgba(244, 63, 94, 0.35);
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
opacity: 1;
|
||||||
|
transform:
|
||||||
|
perspective(1400px) rotateX(35deg) rotateY(360deg) rotateZ(12deg)
|
||||||
|
translate3d(40px, 60px, -120px) scale(0.92);
|
||||||
|
filter: brightness(0.9) saturate(0.9);
|
||||||
|
box-shadow:
|
||||||
|
0 40px 70px rgba(0, 0, 0, 0.6),
|
||||||
|
0 0 22px rgba(244, 63, 94, 0.25);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform:
|
||||||
|
perspective(1400px) rotateX(75deg) rotateY(720deg) rotateZ(38deg)
|
||||||
|
translate3d(140px, 110vh, -400px) scale(0.5);
|
||||||
|
filter: brightness(0.45) saturate(0.6);
|
||||||
|
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-uninstalling {
|
||||||
|
animation: cardFlipFall 1100ms cubic-bezier(0.55, 0.06, 0.68, 0.19) forwards;
|
||||||
|
transform-origin: center center;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
position: relative;
|
||||||
|
z-index: 5;
|
||||||
|
will-change: transform, opacity, filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.card-uninstalling {
|
||||||
|
animation: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 220ms ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ripple {
|
.ripple {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -132,6 +132,26 @@ function getClientCard(clientId) {
|
|||||||
return document.querySelector(`article[data-id="${selectorId}"]`);
|
return document.querySelector(`article[data-id="${selectorId}"]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.__uninstallingClientIds = window.__uninstallingClientIds || new Set();
|
||||||
|
|
||||||
|
function flipFallAndRemove(clientId, { delayMs = 0 } = {}) {
|
||||||
|
const card = getClientCard(clientId);
|
||||||
|
if (!card || card.classList.contains("card-uninstalling")) return;
|
||||||
|
window.__uninstallingClientIds.add(clientId);
|
||||||
|
if (delayMs > 0) card.style.animationDelay = `${delayMs}ms`;
|
||||||
|
card.classList.add("card-uninstalling");
|
||||||
|
|
||||||
|
let removed = false;
|
||||||
|
const cleanup = () => {
|
||||||
|
if (removed) return;
|
||||||
|
removed = true;
|
||||||
|
card.remove();
|
||||||
|
setTimeout(() => window.__uninstallingClientIds.delete(clientId), 2500);
|
||||||
|
};
|
||||||
|
card.addEventListener("animationend", cleanup, { once: true });
|
||||||
|
setTimeout(cleanup, 1100 + delayMs + 400);
|
||||||
|
}
|
||||||
|
|
||||||
function openTagNoteEditor(clientId, currentTag, currentNote) {
|
function openTagNoteEditor(clientId, currentTag, currentNote) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const overlay = document.createElement("div");
|
const overlay = document.createElement("div");
|
||||||
@@ -1440,9 +1460,16 @@ menu.addEventListener("click", async (e) => {
|
|||||||
markManualDisconnect(contextCard);
|
markManualDisconnect(contextCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action === "uninstall") {
|
||||||
|
flipFallAndRemove(contextCard);
|
||||||
|
}
|
||||||
|
|
||||||
const ok = await sendCommand(contextCard, action);
|
const ok = await sendCommand(contextCard, action);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
setTimeout(() => loadWithOptions({ force: true }), 400);
|
// Wait for the flip-fall to finish before refreshing the grid, otherwise
|
||||||
|
// the reload would yank the animating card from layout.
|
||||||
|
const refreshDelay = action === "uninstall" ? 1300 : 400;
|
||||||
|
setTimeout(() => loadWithOptions({ force: true }), refreshDelay);
|
||||||
}
|
}
|
||||||
closeMenu(clearContext);
|
closeMenu(clearContext);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -269,10 +269,14 @@ export function createRenderer({
|
|||||||
|
|
||||||
const hadCards = grid.querySelectorAll("article[data-id]").length > 0;
|
const hadCards = grid.querySelectorAll("article[data-id]").length > 0;
|
||||||
|
|
||||||
|
const uninstallingIds = window.__uninstallingClientIds;
|
||||||
|
|
||||||
items.forEach((client) => {
|
items.forEach((client) => {
|
||||||
seen.add(client.id);
|
seen.add(client.id);
|
||||||
|
if (uninstallingIds && uninstallingIds.has(client.id)) return;
|
||||||
const existing = grid.querySelector(`article[data-id="${client.id}"]`);
|
const existing = grid.querySelector(`article[data-id="${client.id}"]`);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
|
if (existing.classList.contains("card-uninstalling")) return;
|
||||||
const digest = cardDigest(client);
|
const digest = cardDigest(client);
|
||||||
if (existing._cardDigest === digest) return;
|
if (existing._cardDigest === digest) return;
|
||||||
existing._cardDigest = digest;
|
existing._cardDigest = digest;
|
||||||
@@ -283,7 +287,7 @@ export function createRenderer({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Array.from(grid.querySelectorAll("article[data-id]"))
|
Array.from(grid.querySelectorAll("article[data-id]"))
|
||||||
.filter((el) => !seen.has(el.dataset.id))
|
.filter((el) => !seen.has(el.dataset.id) && !el.classList.contains("card-uninstalling"))
|
||||||
.forEach((el) => el.remove());
|
.forEach((el) => el.remove());
|
||||||
|
|
||||||
if (reorder) {
|
if (reorder) {
|
||||||
|
|||||||
Reference in New Issue
Block a user