enhance(frontend): PullToRefreshの改善 (MisskeyIO#218)

This commit is contained in:
まっちゃとーにゅ 2023-11-08 01:39:08 +09:00 committed by GitHub
parent 7b53f66541
commit 3bc84e1fdb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -31,6 +31,7 @@ import { getScrollContainer } from '@/scripts/scroll.js';
const SCROLL_STOP = 10; const SCROLL_STOP = 10;
const MAX_PULL_DISTANCE = Infinity; const MAX_PULL_DISTANCE = Infinity;
const FIRE_THRESHOLD = 230; const FIRE_THRESHOLD = 230;
const FIRE_THRESHOLD_RATIO = 1.1;
const RELEASE_TRANSITION_DURATION = 200; const RELEASE_TRANSITION_DURATION = 200;
const PULL_BRAKE_BASE = 1.5; const PULL_BRAKE_BASE = 1.5;
const PULL_BRAKE_FACTOR = 170; const PULL_BRAKE_FACTOR = 170;
@ -39,9 +40,11 @@ let isPullStart = $ref(false);
let isPullEnd = $ref(false); let isPullEnd = $ref(false);
let isRefreshing = $ref(false); let isRefreshing = $ref(false);
let pullDistance = $ref(0); let pullDistance = $ref(0);
let moveRatio = $ref(0);
let supportPointerDesktop = false; let supportPointerDesktop = false;
let startScreenY: number | null = null; let startScreenY: number | null = null;
let startClientX: number | null = null;
const rootEl = $shallowRef<HTMLDivElement>(); const rootEl = $shallowRef<HTMLDivElement>();
let scrollEl: HTMLElement | null = null; let scrollEl: HTMLElement | null = null;
@ -63,11 +66,20 @@ function getScreenY(event) {
return event.touches[0].screenY; return event.touches[0].screenY;
} }
function getClientX(event) {
if (supportPointerDesktop) {
return event.clientX;
}
return event.touches[0].clientX;
}
function moveStart(event) { function moveStart(event) {
if (!isPullStart && !isRefreshing && !disabled) { if (!isPullStart && !isRefreshing && !disabled && scrollEl?.scrollTop === 0) {
isPullStart = true; isPullStart = true;
startScreenY = getScreenY(event); startScreenY = getScreenY(event);
startClientX = getClientX(event);
pullDistance = 0; pullDistance = 0;
moveRatio = 0;
} }
} }
@ -110,6 +122,7 @@ async function closeContent() {
function moveEnd() { function moveEnd() {
if (isPullStart && !isRefreshing) { if (isPullStart && !isRefreshing) {
startScreenY = null; startScreenY = null;
startClientX = null;
if (isPullEnd) { if (isPullEnd) {
isPullEnd = false; isPullEnd = false;
isRefreshing = true; isRefreshing = true;
@ -126,6 +139,7 @@ function moveEnd() {
} }
function moving(event: TouchEvent | PointerEvent) { function moving(event: TouchEvent | PointerEvent) {
if (!isPullStart && scrollEl?.scrollTop === 0) moveStart(event);
if (!isPullStart || isRefreshing || disabled) return; if (!isPullStart || isRefreshing || disabled) return;
if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance)) { if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance)) {
@ -135,19 +149,23 @@ function moving(event: TouchEvent | PointerEvent) {
return; return;
} }
if (startScreenY === null) { if (startScreenY === null || startClientX === null) {
startScreenY = getScreenY(event); startScreenY = getScreenY(event);
startClientX = getClientX(event);
} }
const moveScreenY = getScreenY(event); const moveScreenY = getScreenY(event);
const moveClientX = getClientX(event);
const moveHeight = moveScreenY - startScreenY!; const moveHeight = moveScreenY - startScreenY!;
const moveWidth = moveClientX - startClientX!;
pullDistance = Math.min(Math.max(moveHeight, 0), MAX_PULL_DISTANCE); pullDistance = Math.min(Math.max(moveHeight, 0), MAX_PULL_DISTANCE);
moveRatio = Math.max(Math.abs(moveHeight), 1) / Math.max(Math.abs(moveWidth), 1);
if (pullDistance > 0) { if (pullDistance > 0 && moveRatio > FIRE_THRESHOLD_RATIO) {
if (event.cancelable) event.preventDefault(); if (event.cancelable) event.preventDefault();
} }
isPullEnd = pullDistance >= FIRE_THRESHOLD; isPullEnd = pullDistance >= FIRE_THRESHOLD && moveRatio > FIRE_THRESHOLD_RATIO;
} }
/** /**
@ -167,47 +185,30 @@ function setDisabled(value) {
} }
function onScrollContainerScroll() { function onScrollContainerScroll() {
const scrollPos = scrollEl!.scrollTop;
// When at the top of the page, disable vertical overscroll so passive touch listeners can take over. // When at the top of the page, disable vertical overscroll so passive touch listeners can take over.
if (scrollPos === 0) { if (scrollEl?.scrollTop === 0) {
scrollEl!.style.touchAction = 'pan-x pan-down pinch-zoom'; scrollEl!.style.touchAction = 'pan-x pan-down pinch-zoom';
registerEventListenersForReadyToPull();
} else { } else {
scrollEl!.style.touchAction = 'auto'; scrollEl!.style.touchAction = 'auto';
unregisterEventListenersForReadyToPull();
} }
} }
function registerEventListenersForReadyToPull() {
if (rootEl == null) return;
rootEl.addEventListener('touchstart', moveStart, { passive: true });
rootEl.addEventListener('touchmove', moving, { passive: false }); // passive: falsepreventDefault使
}
function unregisterEventListenersForReadyToPull() {
if (rootEl == null) return;
rootEl.removeEventListener('touchstart', moveStart);
rootEl.removeEventListener('touchmove', moving);
}
onMounted(() => { onMounted(() => {
if (rootEl == null) return; if (rootEl == null) return;
scrollEl = getScrollContainer(rootEl); scrollEl = getScrollContainer(rootEl);
if (scrollEl == null) return; if (scrollEl == null) return;
scrollEl.addEventListener('scroll', onScrollContainerScroll, { passive: true }); scrollEl.addEventListener('scroll', onScrollContainerScroll, { passive: true });
rootEl.addEventListener('touchstart', moveStart, { passive: true });
rootEl.addEventListener('touchmove', moving, { passive: false }); // passive: falsepreventDefault使
rootEl.addEventListener('touchend', moveEnd, { passive: true }); rootEl.addEventListener('touchend', moveEnd, { passive: true });
registerEventListenersForReadyToPull();
}); });
onUnmounted(() => { onUnmounted(() => {
if (scrollEl) scrollEl.removeEventListener('scroll', onScrollContainerScroll); if (scrollEl) scrollEl.removeEventListener('scroll', onScrollContainerScroll);
if (rootEl == null) return;
unregisterEventListenersForReadyToPull(); rootEl.removeEventListener('touchstart', moveStart);
rootEl.removeEventListener('touchmove', moving);
rootEl.removeEventListener('touchend', moveEnd);
}); });
defineExpose({ defineExpose({