useScroll() composable 通过响应式状态追踪页面(或某个可滚动容器)的滚动情况:包括 scrollPosition(滚动位置)、最后一次滚动的 scrollDirection(滚动方向)、自上次上报以来的 scrollDelta(滚动增量),以及方向最后一次改变的 scrollInflectionPoint(拐点)。
它是 QScrollObserver 组件(该组件正是构建于它之上)和 v-scroll 指令在 setup 代码中的对应物。当你想在自己的组件上、或在任意可滚动容器上获取滚动细节,而又不想为此在模板中额外添加一个节点时,就用这个 composable。
在 SSR 或 SSG 模式的服务端,该 composable 不会监听任何东西:在客户端接管之前,状态会保持它的初始值。
该 composable 也可以在 setup() 之外调用:在 boot 文件、store 或普通模块中。此时没有组件根元素可以作为检测起点,也没有挂载时机可等待,因此请提供一个 scrollTarget(或一个 target 元素);追踪会立即开始,并且不会自行停止:完成后请调用 stopScroll()。
语法
import { useTemplateRef } from 'vue'
import { useScroll } from 'quasar'
setup () {
const scrollTarget = useTemplateRef('scrollTarget') // an Element or a component
const {
scrollPosition, scrollDirection, scrollDirectionChanged,
scrollDelta, scrollInflectionPoint, refreshScroll, stopScroll
} = useScroll({
// all optional:
scrollTarget, // the scroll container; omit it for auto detection
axis: 'vertical', // 'vertical', 'horizontal' or 'both'
debounce: 100, // ms per report; omit for one per frame, 0 for every event
disabled: false, // pause listening
onScroll (details) { // called with the scroll details on every change
// ...
}
})
// ...
}function useScroll(
options?: MaybeRefOrGetter<{
target?: MaybeRefOrGetter<
Element | ComponentPublicInstance | null | undefined
>
scrollTarget?: MaybeRefOrGetter<
Element | Window | string | ComponentPublicInstance | null | undefined
>
axis?: 'vertical' | 'horizontal' | 'both'
debounce?: string | number
disabled?: boolean
onScroll?: (details: {
position: { top: number; left: number }
direction: 'up' | 'down' | 'left' | 'right'
directionChanged: boolean
delta: { top: number; left: number }
inflectionPoint: { top: number; left: number }
}) => void
}>
): {
scrollPosition: ShallowRef<{ top: number; left: number }>
scrollDirection: Ref<'up' | 'down' | 'left' | 'right'>
scrollDirectionChanged: Ref<boolean>
scrollDelta: ShallowRef<{ top: number; left: number }>
scrollInflectionPoint: ShallowRef<{ top: number; left: number }>
refreshScroll: () => void
stopScroll: () => void
}这些响应式状态镜像了 QScrollObserver 所发出(也是 onScroll 所接收)的细节:scrollPosition、scrollDelta 和 scrollInflectionPoint 都是带有 top 和 left 偏移量(以像素为单位)的对象,scrollDirection 是最后一次滚动的方向,而 scrollDirectionChanged 则表明这最后一次滚动是否反转了方向。
追踪的是哪个容器
在不传选项的情况下,该 composable 遵循 Quasar 所有滚动组件和指令使用的同一套算法:从它所在组件的根元素出发(以组件挂载的那一刻为准),向上寻找最近的、带有 scroll、scroll-y 或 overflow-auto CSS 类的父元素;如果一个都找不到,就直接监听页面本身。
scrollTarget直接指定容器:可以是一个 Element(或window)、一个 CSS 选择器,或一个组件实例(代表它的根元素),与滚动组件的scroll-target属性含义相同。target改变自动检测的起点:一个元素或组件的 ref,其最近的可滚动父元素就是你想要的容器。在渲染 fragment(多个根节点)、没有根元素可作起点的组件中,你需要用它。
只要容器可用,第一次上报就会立即发生(即便它当时已经处于滚动状态);onScroll 同样会为此被调用一次,之后仅在所监视的 axis 上位置发生变化时才会再次调用。
不设置 debounce 时,无论浏览器在两帧之间触发了多少次滚动事件,该 composable 每个动画帧最多上报一次。设置 debounce: 0 时,它会在每一次滚动事件上都上报。设置为若干毫秒时,它在每个这么长的时间窗口内最多上报一次;但最后一次变化绝不会被漏掉。
refreshScroll() 会立即读取当前位置,跳过 debounce。你很少会需要它,因为浏览器会自行上报每一次滚动。
stopScroll() 会彻底结束追踪。你同样很少会需要它,因为组件销毁时该 composable 会自行停止。
运行时更改选项
选项可以是一个普通对象、一个 Ref 或一个 getter 函数。普通对象只读取一次。使用 Ref 或 getter 时,该 composable 会追踪选项所读取的任何响应式状态,并在该状态变化时重新应用它们,因此你永远不需要调用任何东西来「更新」它:
- 切换
disabled会暂停和恢复追踪(暂停期间状态保持其最后的值,恢复时如果位置在此期间发生了移动会立即上报) - 把
scrollTarget(或target)指向另一个容器会随之跟踪,并从头开始 - 更改
axis或debounce从下一次滚动起生效 - 替换
onScroll从下一次滚动起生效
import { ref } from 'vue'
import { useScroll } from 'quasar'
setup () {
const paused = ref(false)
const { scrollPosition } = useScroll(() => ({
disabled: paused.value
}))
function pause () { paused.value = true }
function resume () { paused.value = false }
// ...
}示例
下面这个示例通过一个 template ref 追踪某个可滚动容器,并展示 composable 上报的每一个细节:
<template>
<div class="q-pa-md">
<div ref="boxRef" class="scroll box rounded-borders q-mb-md">
<div v-for="n in 30" :key="n" class="q-pa-sm">Line #{{ n }}</div>
</div>
<div class="q-gutter-sm">
<q-badge :label="`top: ${scrollPosition.top}`" />
<q-badge :label="`direction: ${scrollDirection}`" />
<q-badge :label="`delta: ${scrollDelta.top}`" />
<q-badge :label="`inflection point: ${scrollInflectionPoint.top}`" />
<q-badge
:color="scrollDirectionChanged ? 'positive' : 'grey'"
:label="scrollDirectionChanged ? 'direction changed' : 'same direction'"
/>
</div>
</div>
</template>
<script setup>
import { useTemplateRef } from 'vue'
import { useScroll } from 'quasar'
const boxRef = useTemplateRef('boxRef')
const {
scrollPosition,
scrollDirection,
scrollDirectionChanged,
scrollDelta,
scrollInflectionPoint
} = useScroll({ scrollTarget: boxRef })
</script>
<style lang="sass" scoped>
.box
height: 200px
border: 1px solid #fff
outline: 1px solid #000
</style>下面这个示例演示的是页面级滚动:在不传选项的情况下,自动检测会从组件的根元素开始,并在标准布局下最终落到页面本身。把本页向下滚动到示例下方、再往回上滚一点:一旦页面被向下滚动过、而用户又开始向上滚动,「回到顶部」按钮就会被启用:
<template>
<div class="q-pa-md">
<div class="q-gutter-sm q-mb-md">
<q-badge :label="`page top: ${scrollPosition.top}`" />
<q-badge :label="`direction: ${scrollDirection}`" />
</div>
<q-btn
color="primary"
push
icon="keyboard_arrow_up"
label="Back to top"
:disable="scrollPosition.top <= 300 || scrollDirection !== 'up'"
@click="scrollToTop"
/>
</div>
</template>
<script setup>
import { scroll, useScroll } from 'quasar'
// no options: the auto detection starts from this component's root
// element and ends up on the page itself
const { scrollPosition, scrollDirection } = useScroll()
function scrollToTop() {
scroll.setVerticalScrollPosition(window, 0, 300)
}
</script>