From dbeb578b30f5899d201bc136e4658cc9b2793033 Mon Sep 17 00:00:00 2001 From: Curt Grimes Date: Tue, 26 Jul 2022 08:39:19 -0500 Subject: [PATCH] feat(useWindowScroll): support setting scroll position and toggling smooth scrolling Add ability to set scroll values and turn on or off smooth scrolling. fix #1978 --- packages/.vitepress/theme/styles/demo.css | 5 ++ packages/core/useWindowScroll/demo.vue | 34 +++++++++-- packages/core/useWindowScroll/index.md | 46 ++++++++++++++- packages/core/useWindowScroll/index.ts | 69 ++++++++++++++++++++--- 4 files changed, 140 insertions(+), 14 deletions(-) diff --git a/packages/.vitepress/theme/styles/demo.css b/packages/.vitepress/theme/styles/demo.css index 4a1061d317b..f5788f8ef59 100644 --- a/packages/.vitepress/theme/styles/demo.css +++ b/packages/.vitepress/theme/styles/demo.css @@ -60,6 +60,11 @@ padding: 0.25em 0.7em 0.2em 0.7em; } + button.extra-small { + padding: 0.1em 0.25rem; + font-size: 0.7rem; + } + button.orange { --vp-c-brand: #db8742; --vp-c-brand-dark: #ad6e39; diff --git a/packages/core/useWindowScroll/demo.vue b/packages/core/useWindowScroll/demo.vue index c71da862ca8..ed0749a8ae4 100644 --- a/packages/core/useWindowScroll/demo.vue +++ b/packages/core/useWindowScroll/demo.vue @@ -1,21 +1,41 @@ @@ -28,4 +48,10 @@ const { x, y } = useWindowScroll() width: 10000px; height: 10000px; } + +label { + font-size: 0.8rem; + display: flex; + gap: 0.2rem; +} diff --git a/packages/core/useWindowScroll/index.md b/packages/core/useWindowScroll/index.md index 534451c71a0..282752e2634 100644 --- a/packages/core/useWindowScroll/index.md +++ b/packages/core/useWindowScroll/index.md @@ -4,12 +4,54 @@ category: Elements # useWindowScroll -Reactive window scroll +Reactive window scroll. ## Usage -```js +```vue + + + +``` + + +### Smooth scrolling + +Set `behavior: smooth` to enable smooth scrolling. The `behavior` option defaults to `auto`, which means no smooth scrolling. See the `behavior` option on [`window.scrollTo()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo) for more information. +```ts +import { useWindowScroll } from '@vueuse/core' + +const { x, y } = useWindowScroll({ behavior: 'smooth' }) + +// Or as a `ref`: +const smooth = ref(false) +const behavior = computed(() => smooth.value ? 'smooth' : 'auto') +const { x, y } = useWindowScroll({ behavior }) +``` + + +### Custom window + +You can use a custom window with the `window` option: + +```ts +import { useWindowScroll } from '@vueuse/core' + +const { x, y } = useWindowScroll({ window: someOtherWindow }) + +// Or as a `ref`: +const myWindow = ref(window) +const { x, y } = useWindowScroll({ window: myWindow }) ``` diff --git a/packages/core/useWindowScroll/index.ts b/packages/core/useWindowScroll/index.ts index f63459c0b0d..5d3563d1d09 100644 --- a/packages/core/useWindowScroll/index.ts +++ b/packages/core/useWindowScroll/index.ts @@ -1,15 +1,41 @@ -import { ref } from 'vue-demi' +import type { MaybeComputedRef } from '@vueuse/shared' +import { resolveRef, resolveUnref } from '@vueuse/shared' +import type { Ref } from 'vue-demi' +import { computed, ref } from 'vue-demi' import { useEventListener } from '../useEventListener' import type { ConfigurableWindow } from '../_configurable' import { defaultWindow } from '../_configurable' +export interface UseWindowScrollOptions extends ConfigurableWindow { + /** + * Optionally specify a scroll behavior of `auto` (default, not smooth scrolling) or `smooth` (for smooth scrolling) + * + * @default 'auto' + */ + behavior?: MaybeComputedRef +} + +export interface UseWindowScrollReturn { + /** + * The X scroll position of the page + */ + x: Ref + /** + * The Y scroll position of the page + */ + y: Ref +} + /** * Reactive window scroll. * * @see https://vueuse.org/useWindowScroll * @param options */ -export function useWindowScroll({ window = defaultWindow }: ConfigurableWindow = {}) { +export function useWindowScroll(options: UseWindowScrollOptions = {}): UseWindowScrollReturn { + const { window = defaultWindow } = options + const behavior = resolveRef(options.behavior || 'auto') + if (!window) { return { x: ref(0), @@ -17,14 +43,43 @@ export function useWindowScroll({ window = defaultWindow }: ConfigurableWindow = } } - const x = ref(window.pageXOffset) - const y = ref(window.pageYOffset) + const internalX: Ref = ref(0) + const internalY: Ref = 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 ?? window.scrollX + }, + set(x) { + scrollTo(x, undefined) + }, + }) + + const y = computed({ + get() { + return internalY.value ?? window.scrollY + }, + set(y) { + scrollTo(undefined, y) + }, + }) + + function scrollTo(_x: number | undefined, _y: number | undefined) { + window?.scrollTo({ + top: resolveUnref(_y) ?? y.value, + left: resolveUnref(_x) ?? x.value, + behavior: resolveUnref(behavior), + }) + } useEventListener( + window, 'scroll', () => { - x.value = window.pageXOffset - y.value = window.pageYOffset + internalX.value = window.scrollX || 0 + internalY.value = window.scrollY || 0 }, { capture: false, @@ -34,5 +89,3 @@ export function useWindowScroll({ window = defaultWindow }: ConfigurableWindow = return { x, y } } - -export type UseWindowScrollReturn = ReturnType