diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue index 89aca5d29b..f7764998ea 100644 --- a/packages/frontend/src/components/MkPullToRefresh.vue +++ b/packages/frontend/src/components/MkPullToRefresh.vue @@ -43,6 +43,8 @@ const pullDistance = ref(0); let startScreenY: number | null = null; +let moveBySystemCancel: (() => void) | null = null; + const rootEl = useTemplateRef('rootEl'); let scrollEl: HTMLElement | null = null; @@ -127,26 +129,60 @@ function moveStartByTouch(event: TouchEvent) { } function moveBySystem(to: number): Promise { + if (moveBySystemCancel != null) { + moveBySystemCancel(); + moveBySystemCancel = null; + } + return new Promise(r => { const startHeight = pullDistance.value; - const overHeight = pullDistance.value - to; - if (overHeight < 1) { + const overHeight = startHeight - to; + if (Math.abs(overHeight) < 1) { + pullDistance.value = to; r(); return; } - const startTime = Date.now(); - let intervalId = window.setInterval(() => { - const time = Date.now() - startTime; - if (time > RELEASE_TRANSITION_DURATION) { + + let startTime: number | null = null; + let cancelled = false; + moveBySystemCancel = () => { + cancelled = true; + startTime = null; + }; + + const tick = (now: number) => { + if (cancelled) { + r(); + return; + } + if (startTime == null) { + startTime = now; + } + + const time = now - startTime; + if (time >= RELEASE_TRANSITION_DURATION) { pullDistance.value = to; - window.clearInterval(intervalId); + moveBySystemCancel = null; r(); return; } const nextHeight = startHeight - (overHeight / RELEASE_TRANSITION_DURATION) * time; - if (pullDistance.value < nextHeight) return; + if (overHeight > 0) { + if (pullDistance.value < nextHeight) { + window.requestAnimationFrame(tick); + return; + } + } else { + if (pullDistance.value > nextHeight) { + window.requestAnimationFrame(tick); + return; + } + } pullDistance.value = nextHeight; - }, 1); + window.requestAnimationFrame(tick); + }; + + window.requestAnimationFrame(tick); }); } diff --git a/packages/frontend/src/components/MkSwiper.vue b/packages/frontend/src/components/MkSwiper.vue index c55fbfd070..388abddaff 100644 --- a/packages/frontend/src/components/MkSwiper.vue +++ b/packages/frontend/src/components/MkSwiper.vue @@ -131,17 +131,23 @@ function animatePullDistanceTo(to: number, duration = RELEASE_TRANSITION_DURATIO return; } - const startTime = performance.now(); + // DOMHighResTimeStampと合わせるためにDate.now()ではなくperformance.now()を使う + let startTime: DOMHighResTimeStamp | null = null; let cancelled = false; releaseAnimationCancel = () => { cancelled = true; + startTime = null; }; - const tick = (now: number) => { + const tick = (now: DOMHighResTimeStamp) => { if (cancelled) { resolve(); return; } + if (startTime == null) { + startTime = now; + } + const t = Math.min((now - startTime) / duration, 1); // リリース時は軽くイージング(追従中は直接反映) const eased = 1 - Math.pow(1 - t, 3); @@ -297,7 +303,7 @@ function hasSomethingToDoWithXSwipe(el: HTMLElement) { if (style.overflowX === 'scroll') return true; if (style.touchAction === 'pan-x') return true; - if (el.parentElement && el.parentElement !== rootEl.value) { + if (el.parentElement != null && el.parentElement !== rootEl.value) { return hasSomethingToDoWithXSwipe(el.parentElement); } else { return false;