Skip to content

Commit e5fcde2

Browse files
committed
improved miniplayer animation
1 parent 036954e commit e5fcde2

3 files changed

Lines changed: 65 additions & 12 deletions

File tree

.DS_Store

0 Bytes
Binary file not shown.

app.js

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,62 @@ const MiniPlayer = (() => {
190190
};
191191
const MIN_W = 240, MAX_W = 560;
192192

193-
function _attachDrag(el) {
193+
// Spring physics snap animation
194+
function _springSnap(el, fromX, fromY, toX, toY) {
195+
// Cancel any running spring
196+
if (el._springRaf) cancelAnimationFrame(el._springRaf);
197+
198+
const stiffness = 280; // spring stiffness
199+
const damping = 22; // damping coefficient
200+
const mass = 1;
201+
202+
let x = fromX, y = fromY;
203+
let vx = 0, vy = 0;
204+
let lastTime = null;
205+
206+
function step(ts) {
207+
if (!lastTime) { lastTime = ts; el._springRaf = requestAnimationFrame(step); return; }
208+
const dt = Math.min((ts - lastTime) / 1000, 0.032); // cap at 32ms
209+
lastTime = ts;
210+
211+
// Spring force
212+
const ax = (-stiffness * (x - toX) - damping * vx) / mass;
213+
const ay = (-stiffness * (y - toY) - damping * vy) / mass;
214+
215+
vx += ax * dt;
216+
vy += ay * dt;
217+
x += vx * dt;
218+
y += vy * dt;
219+
220+
el.style.left = x + 'px';
221+
el.style.top = y + 'px';
222+
223+
// Check if settled (velocity and displacement both tiny)
224+
const settled = Math.abs(x - toX) < 0.15 && Math.abs(y - toY) < 0.15
225+
&& Math.abs(vx) < 0.5 && Math.abs(vy) < 0.5;
226+
227+
if (!settled && document.body.contains(el)) {
228+
el._springRaf = requestAnimationFrame(step);
229+
} else {
230+
el.style.left = toX + 'px';
231+
el.style.top = toY + 'px';
232+
el._springRaf = null;
233+
// Subtle landing pulse
234+
el.style.transition = 'transform 0.18s cubic-bezier(0.34,1.56,0.64,1)';
235+
el.style.transform = 'scale(1.03)';
236+
setTimeout(() => {
237+
if (el) {
238+
el.style.transform = 'scale(1)';
239+
setTimeout(() => { if (el) el.style.transition = ''; }, 200);
240+
}
241+
}, 80);
242+
}
243+
}
244+
245+
el._springRaf = requestAnimationFrame(step);
246+
}
247+
248+
function _attachDrag(el) {
194249
// Drag is initiated from the pill (pointer-events: auto), not the full handle container
195250
const handle = el.querySelector('.mp-drag-pill');
196251
if (!handle) return;
@@ -238,7 +293,7 @@ const MiniPlayer = (() => {
238293
_drag.active = false;
239294
el.classList.remove('is-dragging');
240295

241-
// Snap to nearest corner
296+
// Determine nearest corner
242297
const W = window.innerWidth, H = window.innerHeight;
243298
const elW = el.offsetWidth, elH = el.offsetHeight;
244299
const curLeft = parseFloat(el.style.left) || (W - elW - 28);
@@ -247,15 +302,14 @@ const MiniPlayer = (() => {
247302
const cy = curTop + elH / 2;
248303
const MARGIN = 20;
249304

250-
const snapLeft = cx < W / 2 ? MARGIN : W - elW - MARGIN;
251-
const snapTop = cy < H / 2 ? MARGIN : H - elH - MARGIN;
305+
const targetLeft = cx < W / 2 ? MARGIN : W - elW - MARGIN;
306+
const targetTop = cy < H / 2 ? MARGIN : H - elH - MARGIN;
252307

253-
el.classList.add('snap-back');
254-
el.style.left = snapLeft + 'px';
255-
el.style.top = snapTop + 'px';
256308
el.style.right = 'auto';
257309
el.style.bottom = 'auto';
258-
setTimeout(() => el && el.classList.remove('snap-back'), 450);
310+
311+
// JS spring animation — much better than CSS transition
312+
_springSnap(el, curLeft, curTop, targetLeft, targetTop);
259313
}
260314

261315
handle.addEventListener('mousedown', onPointerDown);

styles.css

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -893,11 +893,10 @@ header.hero p { color: var(--muted); margin-top: 16px; font-size: 1.15rem; max-w
893893
border-color: rgba(0,0,0,0.2);
894894
}
895895

896-
/* Snap-to-corner spring animation */
896+
/* Snap-to-corner — handled by JS spring physics (_springSnap)
897+
CSS transition only used for resize width changes */
897898
.miniplayer.snap-back {
898-
transition: left 0.45s cubic-bezier(0.34, 1.56, 0.64, 1),
899-
top 0.45s cubic-bezier(0.34, 1.56, 0.64, 1),
900-
width 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
899+
/* intentionally empty — spring is JS-driven */
901900
}
902901

903902
/* ── Copy toast ─────────────────────────────────────────────────────────── */

0 commit comments

Comments
 (0)