Skip to content

Commit

Permalink
feat: perf useScroll
Browse files Browse the repository at this point in the history
  • Loading branch information
Lionad-Morotar committed Dec 19, 2023
1 parent 18dbc98 commit 3cfc536
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 42 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"build:watch": "pnpm clean && pnpm check:types && vite build --watch",
"check:types": "vue-tsc --noEmit",
"prepare": "husky install",
"auto:publish": "git checkout dev | pnpm build | git branch -D release | git checkout -b release | git merge dev | npm version patch | pnpm build | pnpm publish | git push --set-upstream origin release | git checkout dev | git merge release | git push origin dev"
"auto:publish": "git checkout dev | git branch -D release | git checkout -b release | git merge dev | npm version patch | pnpm build | pnpm publish | git push --set-upstream origin release | git checkout dev | git merge release | git push origin dev"
},
"dependencies": {
"@vueuse/core": "^10.7.0",
Expand Down
2 changes: 1 addition & 1 deletion play/src/vxe-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ const formatterSex = ({ cellValue }: any) => {
let count = 0
let parentId = 0
const listCount = 2000
const listCount = 500
const getTableData = () => {
const res = Array(listCount).fill(0).map((x) => {
const res = {
Expand Down
6 changes: 4 additions & 2 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import useElementSize from './useElementSize'
import useElementHover from './useElementHover'
import useScroll from './useScroll'
import useScrollbar from './useScrollbar'
import useNativeScrollbar from './useNativeScrollbar'

export * from './useElementSize'
export * from './useElementHover'
export * from './useScroll'
export * from './useScrollbar'
export * from './useNativeScrollbar'

const useScrollbars = useScrollbar

export { useElementSize, useElementHover, useScrollbar, useScrollbars, useNativeScrollbar }
export { useElementSize, useElementHover, useScroll, useScrollbar, useScrollbars, useNativeScrollbar }

const index = { useElementSize, useElementHover, useScrollbar, useScrollbars, useNativeScrollbar }
const index = { useElementSize, useElementHover, useScroll, useScrollbar, useScrollbars, useNativeScrollbar }

export default index
213 changes: 213 additions & 0 deletions src/hooks/useScroll/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import { computed, ref } from 'vue-demi'
import { useEventListener } from '@vueuse/core'
import type { MaybeRefOrGetter } from '@vueuse/shared'
import { tryOnMounted, noop, toValue, useDebounceFn, useThrottleFn } from '@vueuse/shared'

const defaultWindow = typeof window === 'undefined' ? undefined : window

export interface UseScrollOptions {
window?: any
/**
* Throttle time for scroll event, it’s disabled by default.
*
* @default 0
*/
throttle?: number

/**
* The check time when scrolling ends.
* This configuration will be setting to (throttle + idle) when the `throttle` is configured.
*
* @default 200
*/
idle?: number

/**
* Offset arrived states by x pixels
*
*/
offset?: {
left?: number
right?: number
top?: number
bottom?: number
}

/**
* Trigger it when scrolling.
*
*/
onScroll?: (e: Event) => void

/**
* Trigger it when scrolling ends.
*
*/
onStop?: (e: Event) => void

/**
* Listener options for scroll event.
*
* @default {capture: false, passive: true}
*/
eventListenerOptions?: boolean | AddEventListenerOptions

/**
* Optionally specify a scroll behavior of `auto` (default, not smooth scrolling) or
* `smooth` (for smooth scrolling) which takes effect when changing the `x` or `y` refs.
*
* @default 'auto'
*/
behavior?: MaybeRefOrGetter<ScrollBehavior>
}

/**
* perf reactive use-scroll instead of @vueuse/core's
*
* @see https://vueuse.org/useScroll
* @param element
* @param options
*/

export default function useScroll(
element: MaybeRefOrGetter<HTMLElement | SVGElement | Window | Document | null | undefined>,
options: UseScrollOptions = {},
) {
const {
throttle = 0,
idle = 200,
onStop = noop,
onScroll = noop,
eventListenerOptions = {
capture: false,
passive: true,
},
behavior = 'auto',
window = defaultWindow,
} = options

const internalX = ref(0)
const internalY = ref(0)

// Use a computed for x and y because we want to write the value to the refs
// during a `scrollTo()` without firing additional `scrollTo()`s in the process.
const x = computed({
get() {
return internalX.value
},
set(x) {
scrollTo(x, undefined)
},
})

const y = computed({
get() {
return internalY.value
},
set(y) {
scrollTo(undefined, y)
},
})

function scrollTo(_x: number | undefined, _y: number | undefined) {
if (!window)
return

const _element = toValue(element)
if (!_element)
return

(_element instanceof Document ? window.document.body : _element)?.scrollTo({
top: toValue(_y) ?? y.value,
left: toValue(_x) ?? x.value,
behavior: toValue(behavior),
})
}

const isScrolling = ref(false)

const onScrollEnd = (e: Event) => {
// dedupe if support native scrollend event
if (!isScrolling.value)
return

isScrolling.value = false
onStop(e)
}
const onScrollEndDebounced = useDebounceFn(onScrollEnd, throttle + idle)

const setArrivedState = (target: HTMLElement | SVGElement | Window | Document | null | undefined) => {
if (!window)
return

const el = (
(target as Window).document
? (target as Window).document.documentElement
: (target as Document).documentElement ?? target
) as HTMLElement

const scrollLeft = el.scrollLeft

internalX.value = scrollLeft

let scrollTop = el.scrollTop

// patch for mobile compatible
if (target === window.document && !scrollTop)
scrollTop = window.document.body.scrollTop

internalY.value = scrollTop
}

const onScrollHandler = (e: Event) => {
if (!window)
return

const eventTarget = (
(e.target as Document).documentElement ?? e.target
) as HTMLElement

setArrivedState(eventTarget)

isScrolling.value = true
onScrollEndDebounced(e)
onScroll(e)
}

useEventListener(
element,
'scroll',
throttle ? useThrottleFn(onScrollHandler, throttle, true, false) : onScrollHandler,
eventListenerOptions,
)

tryOnMounted(() => {
const _element = toValue(element)
if (!_element)
return

setArrivedState(_element)
})

useEventListener(
element,
'scrollend',
onScrollEnd,
eventListenerOptions,
)

return {
x,
y,
isScrolling,
measure() {
const _element = toValue(element)

if (window && _element)
setArrivedState(_element)
},
}
}

export type UseScrollReturn = ReturnType<typeof useScroll>

Loading

0 comments on commit 3cfc536

Please sign in to comment.