From 5348eda1ed7aeeb62af7004080108d13ea056d8e Mon Sep 17 00:00:00 2001 From: thislight Date: Wed, 9 Oct 2024 14:38:25 +0800 Subject: [PATCH] PullDownToRefresh: rewrite spring animation - fix #12 --- src/timelines/PullDownToRefresh.tsx | 46 +++++++++++++++++++---------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/timelines/PullDownToRefresh.tsx b/src/timelines/PullDownToRefresh.tsx index e63aaa2..ccf8294 100644 --- a/src/timelines/PullDownToRefresh.tsx +++ b/src/timelines/PullDownToRefresh.tsx @@ -46,41 +46,56 @@ const PullDownToRefresh: Component<{ let lts = -1; let ds = 0; let holding = false; - const M = 1; - const K = -10; - const D = -10; + const K = 5; const updatePullDown = (ts: number) => { released = false; try { const x = untrack(pullDown); const dt = lts !== -1 ? ts - lts : 1 / 60; - const fs = (ds / Math.pow(dt, 2)) * M; - const fh = (x + ds) * K + D * v; - const f = fs + fh; - const a = f / M; - v += a * dt; - if (holding && v < 0) { - v = 0 - } + const vspring = holding ? 0 : K * x * dt; + v = ds / dt - vspring; + setPullDown(x + v * dt); + if (Math.abs(x) > 1 || Math.abs(v) > 1) { requestAnimationFrame(updatePullDown); } else { v = 0; lts = -1; } - if (!holding && untrack(pullDownDistance) >= 160 && !props.loading && props.onRefresh) { - setTimeout(props.onRefresh, 0) + + if ( + !holding && + untrack(pullDownDistance) >= 160 && + !props.loading && + props.onRefresh + ) { + setTimeout(props.onRefresh, 0); + // FIXME: this may trigger at multiple frames } } finally { ds = 0; released = true; } }; + + let wheelTimeout: ReturnType | undefined; + + const onWheelNotUpdated = () => { + wheelTimeout = undefined; + holding = false; + }; + const handleLinkedWheel = (event: WheelEvent) => { const scrollTop = (event.target as HTMLElement).scrollTop; if (scrollTop >= 0 && scrollTop < 1) { - ds = -(event.deltaY / window.devicePixelRatio / 4); + event.preventDefault() + ds = -(event.deltaY / window.devicePixelRatio / 2); + holding = true; + if (wheelTimeout) { + clearTimeout(wheelTimeout); + } + wheelTimeout = setTimeout(onWheelNotUpdated, 200); if (released) { released = false; requestAnimationFrame(updatePullDown); @@ -107,6 +122,7 @@ const PullDownToRefresh: Component<{ return; } const item = event.targetTouches.item(0)!; + event.preventDefault(); if (lastTouchId && item.identifier !== lastTouchId) { lastTouchId = undefined; lastTouchScreenY = 0; @@ -114,7 +130,7 @@ const PullDownToRefresh: Component<{ } holding = true; if (lastTouchScreenY !== 0) { - ds = item.screenY - lastTouchScreenY; + ds = (item.screenY - lastTouchScreenY) / window.devicePixelRatio; } lastTouchScreenY = item.screenY; if (released) {