From 2e6dbca06dc38576308297d0ba6331ec4274e8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=98=89=E6=B6=B5?= Date: Tue, 14 Jan 2020 17:20:15 +0800 Subject: [PATCH] Revert "chore: remove hooks ooops!" This reverts commit 8d4d965a85275373375bcd04563ec854c66d2f08. --- src/hooks/use-click-outside.ts | 21 ++++++++++++ src/hooks/use-global-event.ts | 46 +++++++++++++++++++++++++ src/hooks/use-lock-scroll.ts | 55 +++++++++++++++++++++++++++++ src/hooks/use-touch.ts | 63 ++++++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+) create mode 100644 src/hooks/use-click-outside.ts create mode 100644 src/hooks/use-global-event.ts create mode 100644 src/hooks/use-lock-scroll.ts create mode 100644 src/hooks/use-touch.ts diff --git a/src/hooks/use-click-outside.ts b/src/hooks/use-click-outside.ts new file mode 100644 index 000000000..db64eb2a0 --- /dev/null +++ b/src/hooks/use-click-outside.ts @@ -0,0 +1,21 @@ +import { Ref } from 'vue'; +import { useGlobalEvent } from './use-global-event'; + +export type UseClickOutsideOpitons = { + event: string; + callback: EventListener; + element: Ref; + flag?: Ref; +}; + +export function useClickOutside(options: UseClickOutsideOpitons) { + const { event = 'click', callback, element, flag } = options; + + function onClick(event: Event) { + if (!element.value.contains(event.target as Node)) { + callback(event); + } + } + + useGlobalEvent(document, event, onClick, false, flag); +} diff --git a/src/hooks/use-global-event.ts b/src/hooks/use-global-event.ts new file mode 100644 index 000000000..319c043bf --- /dev/null +++ b/src/hooks/use-global-event.ts @@ -0,0 +1,46 @@ +import { on, off } from '../utils/dom/event'; +import { + Ref, + watch, + onMounted, + onActivated, + onUnmounted, + onDeactivated +} from 'vue'; + +export function useGlobalEvent( + target: EventTarget, + event: string, + handler: EventListener, + passive = false, + flag?: Ref +) { + let binded: boolean; + + function add() { + if (binded || (flag && !flag.value)) { + return; + } + + on(target, event, handler, passive); + binded = true; + } + + function remove() { + if (binded) { + off(target, event, handler); + binded = false; + } + } + + if (flag) { + watch(() => { + flag.value ? add() : remove(); + }); + } + + onMounted(add); + onActivated(add); + onUnmounted(remove); + onDeactivated(remove); +} diff --git a/src/hooks/use-lock-scroll.ts b/src/hooks/use-lock-scroll.ts new file mode 100644 index 000000000..9783b2090 --- /dev/null +++ b/src/hooks/use-lock-scroll.ts @@ -0,0 +1,55 @@ +import { useTouch } from './use-touch'; +import { getScroller } from '../utils/dom/scroll'; +import { on, off, preventDefault } from '../utils/dom/event'; + +let count = 0; +const CLASSNAME = 'van-overflow-hidden'; + +export function useLockScroll(element: HTMLElement) { + const { start, move, deltaY, direction } = useTouch(); + + function onTouchMove(event: TouchEvent) { + move(event); + + if (direction.value !== 'vertical') { + return; + } + + let prevent = false; + const up = deltaY.value < 0; + const scroller = getScroller(event.target as HTMLElement, element); + const { scrollTop, scrollHeight, offsetHeight } = scroller as HTMLElement; + + if (scrollTop === 0) { + prevent = up && offsetHeight < scrollHeight; + } else if (scrollTop + offsetHeight >= scrollHeight) { + prevent = !up; + } + + if (prevent) { + preventDefault(event, true); + } + } + + function lock() { + if (!count) { + document.body.classList.add(CLASSNAME); + } + + count++; + on(document, 'touchstart', start); + on(document, 'touchmove', onTouchMove); + } + + lock(); + + return function unlock() { + count--; + off(document, 'touchstart', start); + off(document, 'touchmove', onTouchMove); + + if (!count) { + document.body.classList.remove(CLASSNAME); + } + }; +} diff --git a/src/hooks/use-touch.ts b/src/hooks/use-touch.ts new file mode 100644 index 000000000..aaaa5fb5c --- /dev/null +++ b/src/hooks/use-touch.ts @@ -0,0 +1,63 @@ +import { ref } from 'vue'; + +const MIN_DISTANCE = 10; + +function getDirection(x: number, y: number) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal'; + } + + if (y > x && y > MIN_DISTANCE) { + return 'vertical'; + } + + return ''; +} + +export function useTouch() { + const startX = ref(0); + const startY = ref(0); + const deltaX = ref(0); + const deltaY = ref(0); + const offsetX = ref(0); + const offsetY = ref(0); + const direction = ref(''); + + function reset() { + direction.value = ''; + deltaX.value = 0; + deltaY.value = 0; + offsetX.value = 0; + offsetY.value = 0; + } + + function start(event: TouchEvent) { + reset(); + startX.value = event.touches[0].clientX; + startY.value = event.touches[0].clientY; + } + + function move(event: TouchEvent) { + const touch = event.touches[0]; + deltaX.value = touch.clientX - this.startX; + deltaY.value = touch.clientY - this.startY; + offsetX.value = Math.abs(this.deltaX); + offsetY.value = Math.abs(this.deltaY); + + if (!direction.value) { + direction.value = getDirection(offsetX.value, offsetY.value); + } + } + + return { + move, + start, + startX, + startY, + deltaX, + deltaY, + offsetX, + offsetY, + direction + }; +}