Skip to content
94 changes: 75 additions & 19 deletions packages/frontend/src/components/MkPullToRefresh.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ const pullDistance = ref(0);

let startScreenY: number | null = null;

let moveBySystemCancel: (() => void) | null = null;
let moveBySystemRafId: number | null = null;

const onMouseMove = (event: MouseEvent) => moving(event);
const onMouseUp = () => {
window.removeEventListener('mousemove', onMouseMove);
onPullRelease();
};

const onTouchMove = (event: TouchEvent) => moving(event);
const onTouchEnd = () => {
window.removeEventListener('touchmove', onTouchMove);
onPullRelease();
};

const rootEl = useTemplateRef('rootEl');
let scrollEl: HTMLElement | null = null;

Expand Down Expand Up @@ -97,11 +112,8 @@ function moveStartByMouse(event: MouseEvent) {
startScreenY = getScreenY(event);
pullDistance.value = 0;

window.addEventListener('mousemove', moving, { passive: true });
window.addEventListener('mouseup', () => {
window.removeEventListener('mousemove', moving);
onPullRelease();
}, { passive: true, once: true });
window.addEventListener('mousemove', onMouseMove, { passive: true });
window.addEventListener('mouseup', onMouseUp, { passive: true, once: true });
}

function moveStartByTouch(event: TouchEvent) {
Expand All @@ -119,34 +131,70 @@ function moveStartByTouch(event: TouchEvent) {
startScreenY = getScreenY(event);
pullDistance.value = 0;

window.addEventListener('touchmove', moving, { passive: true });
window.addEventListener('touchend', () => {
window.removeEventListener('touchmove', moving);
onPullRelease();
}, { passive: true, once: true });
window.addEventListener('touchmove', onTouchMove, { passive: true });
window.addEventListener('touchend', onTouchEnd, { passive: true, once: true });
}

function moveBySystem(to: number): Promise<void> {
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: DOMHighResTimeStamp | null = null;
let cancelled = false;
moveBySystemCancel = () => {
cancelled = true;
startTime = null;
if (moveBySystemRafId != null) {
window.cancelAnimationFrame(moveBySystemRafId);
moveBySystemRafId = null;
}
};

const tick = (now: DOMHighResTimeStamp) => {
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;
moveBySystemRafId = null;
r();
return;
}
const nextHeight = startHeight - (overHeight / RELEASE_TRANSITION_DURATION) * time;
if (pullDistance.value < nextHeight) return;
if (overHeight > 0) {
if (pullDistance.value < nextHeight) {
moveBySystemRafId = window.requestAnimationFrame(tick);
return;
}
} else {
if (pullDistance.value > nextHeight) {
moveBySystemRafId = window.requestAnimationFrame(tick);
return;
}
}
pullDistance.value = nextHeight;
}, 1);
moveBySystemRafId = window.requestAnimationFrame(tick);
};

moveBySystemRafId = window.requestAnimationFrame(tick);
});
}

Expand Down Expand Up @@ -230,6 +278,14 @@ onMounted(() => {
});

onUnmounted(() => {
if (moveBySystemCancel != null) {
moveBySystemCancel();
moveBySystemCancel = null;
}
moveBySystemRafId = null;
// pull中にwindowへ登録したリスナーが残るのを防ぐ
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('touchmove', onTouchMove);
unlockDownScroll();
if (rootEl.value) rootEl.value.removeEventListener('mousedown', moveStartByMouse);
if (rootEl.value) rootEl.value.removeEventListener('touchstart', moveStartByTouch);
Expand Down
Loading
Loading