enhance(frontend): PullToRefreshの改善 (MisskeyIO#218)
This commit is contained in:
parent
7b53f66541
commit
3bc84e1fdb
|
@ -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: falseにしないとpreventDefaultが使えない
|
|
||||||
}
|
|
||||||
|
|
||||||
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: falseにしないとpreventDefaultが使えない
|
||||||
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({
|
||||||
|
|
Loading…
Reference in a new issue