4.7 Sensors -- useScroll

news/2024/9/17 7:45:49/ 标签: javascript, 前端, vue.js

4.7 Sensors – useScroll

https://vueuse.org/core/useScroll/

作用

响应式的监听滚动位置和状态。

官方示例

<script setup lang="ts">javascript">
import { useScroll } from '@vueuse/core'const el = ref<HTMLElement | null>(null)
const { x, y, isScrolling, arrivedState, directions } = useScroll(el)
</script><template><div ref="el" />
</template>
  • 带偏移量的版本
const { x, y, isScrolling, arrivedState, directions } = useScroll(el, {offset: { top: 30, bottom: 30, right: 30, left: 30 },
})
  • 手动设置滚动位置
<script setup lang="ts">javascript">
import { useScroll } from '@vueuse/core'const el = ref<HTMLElement | null>(null)
const { x, y } = useScroll(el)
</script><template><div ref="el" /><!-- 手动修改滚动偏移量 --><button @click="x += 10">Scroll right 10px</button><button @click="y += 10">Scroll down 10px</button>
</template>
  • 平滑滚动

    设置 behavior: smooth 实现平滑滚动.。默认行为是auto ,也就是非平滑滚动。更多滚动行为,请看https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo

import { useScroll } from '@vueuse/core'const el = ref<HTMLElement | null>(null)
const { x, y } = useScroll(el, { behavior: 'smooth' })// Or as a `ref`:
const smooth = ref(false)
const behavior = computed(() => smooth.value ? 'smooth' : 'auto')
// 通过options传递
const { x, y } = useScroll(el, { behavior })
  • 无渲染组件代码,同样支持传递回调函数和数组
<script setup lang="ts">javascript">
import type { UseScrollReturn } from '@vueuse/core'
import { vScroll } from '@vueuse/components'const data = ref([1, 2, 3, 4, 5, 6])function onScroll(state: UseScrollReturn) {console.log(state) // {x, y, isScrolling, arrivedState, directions}
}
</script><template><div v-scroll="onScroll"><div v-for="item in data" :key="item">{{ item }}</div></div><!-- with options --><div v-scroll="[onScroll, { throttle: 10 }]"><div v-for="item in data" :key="item">{{ item }}</div></div>
</template>

源码分析

  • 这个hook的参数比较常用,先看一下
export interface UseScrollOptions {/*** 对滚动事件进行节流,默认不节流。也就是throttle时间段内,只会触发一次* @default 0毫秒。*/throttle?: number/*** 滚动结束后多久进行检查* 如果设置了throttle,这个值等于如果设置了throttle+idle* @default 200*/idle?: number/*** left 表示距离左边距多远,arrived状态会变成true。其他方向类推。*/offset?: {left?: numberright?: numbertop?: numberbottom?: number}/*** 滚动时触发的事件*/onScroll?: (e: Event) => void/*** 滚动结束触发的事件*/onStop?: (e: Event) => void/*** 滚动事件监听器的配置* @default {capture: false, passive: true}*/eventListenerOptions?: boolean | AddEventListenerOptions/*** 平滑滚动还是立即跳转* @default 'auto'*/behavior?: MaybeComputedRef<ScrollBehavior>
}/*** 我们必须检查滚动量是否足够接近某个阈值,以便更准确地计算arrivedState。* 这是因为scrollTop/scrollLeft是整数的数字,而scrollHeight/scrollWidth和clienttheight /clientWidth是四舍五入的。* https://developer.mozilla.org/enUS/docs/Web/API/Element/scrollHeight#determine_if_an_element_has_been_totally_scrolled*/
const ARRIVED_STATE_THRESHOLD_PIXELS = 1
  • 再看一下代码实现,大量代码都是用来定义变量的。
/**
* 代码中一些函数的定义
*/
// 1 空函数
export const noop = () => {}// 2 防抖函数:如果多次调用,只有最后一次起作用,会在最后一次调用后,经过一段时候后触发回调。
useDebounceFn()// 3 节流函数:如果多次调用,那么在一个时间段内,只有第一次起作用。到了下个时间段,依旧只有第一次起作用。
useThrottleFn()
export function useScroll(element: MaybeComputedRef<HTMLElement | SVGElement | Window | Document | null | undefined>,options: UseScrollOptions = {},
) {/*** 处理用户传递的参数*/const {throttle = 0,idle = 200,onStop = noop,onScroll = noop,offset = {left: 0,right: 0,top: 0,bottom: 0,},eventListenerOptions = {capture: false,passive: true,},behavior = 'auto',} = options/*** x方向和y方向的偏移量。默认是0,也就是视口在页面左上角。*/const internalX = ref(0)const internalY = ref(0)/*** 提供给外部的计算属性。当用户设置x和y的时候,要出发滚动事件。* 在'scrollTo()'期间,不会在进程中触发额外的'scrollTo()'。*/const x = computed({get() {return internalX.value},set(x) {scrollTo(x, undefined)},})const y = computed({get() {return internalY.value},set(y) {scrollTo(undefined, y)},})/*** 使用 scrollTo 方法来滚动,传递目标位置的高度、左边距、滚动方式。*/function scrollTo(_x: number | undefined, _y: number | undefined) {const _element = resolveUnref(element)if (!_element)return(_element instanceof Document ? document.body : _element)?.scrollTo({top: resolveUnref(_y) ?? y.value,left: resolveUnref(_x) ?? x.value,behavior: resolveUnref(behavior),})}/*** 是否在滚动中*/const isScrolling = ref(false)/*** 四边的到达状态,默认左上角。*/const arrivedState = reactive({left: true,right: false,top: true,bottom: false,})/*** 朝着哪个方向滚动,默认哪边都不是。*/const directions = reactive({left: false,right: false,top: false,bottom: false,})/*** 滚动结束后,把滚动方向都设置为false。设置防抖时间:throttle + idle、* 同时调用用户传递的onStop*/const onScrollEnd = useDebounceFn((e: Event) => {isScrolling.value = falsedirections.left = falsedirections.right = falsedirections.top = falsedirections.bottom = falseonStop(e)}, throttle + idle)// ......// 最重要的滚动函数单独来看/*** 监听目标的scroll事件。如果发生了滚动,先看throttle的值* 如果设置了throttle,那么throttle的时间段内只调用一次。否则滚动即调用onScrollHandler。*/useEventListener(element,'scroll',throttle ? useThrottleFn(onScrollHandler, throttle, true, false) : onScrollHandler,eventListenerOptions,)return {x,y,isScrolling,arrivedState,directions,}
}
/**
* 滚动过程中触发的函数
*/
const onScrollHandler = (e: Event) => {const eventTarget = (e.target === document ? (e.target as Document).documentElement : e.target) as HTMLElementconst scrollLeft = eventTarget.scrollLeft// 如果滚动后的左边距小于原来的边距,说明时往左边滚动了。见下图1directions.left = scrollLeft < internalX.valuedirections.right = scrollLeft > internalY.value// 是否到达左边距。如果用户设置了offset.left,那么滚动后的左边距小于这个值就代表抵达到左边了arrivedState.left = scrollLeft <= 0 + (offset.left || 0)// 在从左往右滚动的过程中,这几个值变化如下图2// 可以简单理解,clientWidth是用户可见区域的宽度,scrollWidth是内容区真正的宽度。见下图3arrivedState.right= scrollLeft + eventTarget.clientWidth >= eventTarget.scrollWidth - (offset.right || 0) - ARRIVED_STATE_THRESHOLD_PIXELS// 实时更新滚动的距离internalX.value = scrollLeftlet scrollTop = eventTarget.scrollTop// 移动端兼容if (e.target === document && !scrollTop)scrollTop = document.body.scrollTop// 上下的处理和左右是一致的directions.top = scrollTop < internalY.valuedirections.bottom = scrollTop > internalY.valuearrivedState.top = scrollTop <= 0 + (offset.top || 0)arrivedState.bottom= scrollTop + eventTarget.clientHeight >= eventTarget.scrollHeight - (offset.bottom || 0) - ARRIVED_STATE_THRESHOLD_PIXELSinternalY.value = scrollTop// 滚动中状态设置为trueisScrolling.value = true// 不断触发scrollEnd事件,但是这个事件设置了防抖,只有在停顿超过throttle + idle时间后才会触发状态修改onScrollEnd(e)// 实时调用用户传递的函数onScroll(e)
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


http://www.ppmy.cn/news/1522679.html

相关文章

Spring常用中间件

1. 数据库中间件 &#xff08;1&#xff09;MySQL: 常用的关系型数据库&#xff0c;支持JDBC和JPA。 &#xff08;2&#xff09;PostgreSQL: 功能强大的开源关系型数据库&#xff0c;支持复杂查询。 &#xff08;3&#xff09;MongoDB: NoSQL数据库&#xff0c;适合存储非结构化…

【Rust练习】13.数组

练习题来自&#xff1a;https://practice-zh.course.rs/compound-types/array.html 1 fn main() {// 使用合适的类型填空let arr: __ [1, 2, 3, 4, 5];// 修改以下代码&#xff0c;让它顺利运行assert!(arr.len() 4); }显然这个数组的长度是5. fn main() {// 使用合适的类…

ELK学习笔记(三)——使用Filebeat8.15.0收集日志

使用Filebeat收集日志 前面教程已经把ElasticSearch和Kibana部署完毕&#xff0c;接着我们就要使用filebeat去收集我们的java服务日志&#xff0c;这里首先介绍一下ELK和EFK的区别。 一、ELK和EFK的区别 在收集和处理日志时&#xff0c;使用 ELK&#xff08;Elasticsearch, …

8. GIS数据分析师岗位职责、技术要求和常见面试题

本系列文章目录&#xff1a; 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…

tabBar设置底部菜单选项以及iconfont图标

tabBartabBar属性:设置底部 tab 的表现 ​ ​ ​ ​ 首先在pages.json页面写一个tabBar对象,里面放入list对象数组,里面至少要有2个、最多5个 tab, 如果只有一个tab的话,H5(浏览器)依然可以显示底部有一个导航栏,如果没有,需要重启后才有,小程序则报错,只有2个以上才可以…

C# 窗口页面布局

1.Groupbox 单机鼠标右键&#xff0c;置于底层 2.Label 在右方属性中修改名称 3.ComboBox 点击属性中的集合&#xff0c;可以添加选择项 4.CheckBox 在属性中修改名称 5.RichTextBox 富文本 在属性中修改名称与区域 6.StatusStrip 状态栏 将AutoSize改成false就可以修改…

基于Java的宿舍报修管理系统的设计与实现(论文+源码)_kaic

基于Java的宿舍报修管理系统的设计与实现(论文源码)_kaic 摘  要 随着教育改革‎‏的不断‎‏深入&#xff0c;‎‏学校宿‎‏舍的管‎‏理体系‎‏也在不‎‏断地完‎‏善&#xff0c;校园后勤服务是学校管理的重要工作&#xff0c;学校提供优秀的后勤服务&#xff0c;能提…

C语言代码练习(第十七天)

今日练习&#xff1a; 45、输出100-1000之间所有的“水仙花数”&#xff0c;所为的水仙花数是一个三位数&#xff0c;其各位数字立方和等于该数本身。例如153是一个水仙花数。因为1*1*15*5*53*3*3 46、一个数如果恰好等于它的因子之和&#xff0c;这个数就称为"完数"…

AI视频百万播放,用这个免费的AI工具,3步教你制作爆款治愈系视频!(附完整教程)

大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 今天一位粉丝发了一个视频链接&#xff0c;问这类治愈系风景的…

centos基本命令

当前登录用户&#xff08;root&#xff09; 用户组 其它用户 rwxr-xr-x cd 后加/目录名/子目录 切换到目录 cd .. 切换到父目录 CentOS Windows $>ls 查看某个目录有哪文件和目录 cmd>dir …

机器学习:多种算法处理填充后的数据

在机器学习中&#xff0c;填充数据&#xff08;即处理缺失值&#xff09;后&#xff0c;选择合适的算法并优化模型以提高召回率是一个常见的任务。召回率是指模型正确识别的正例占所有实际正例的比例。 代码思路&#xff1a; 数据预处理&#xff1a; 导入填充后的数据 …

Python | Leetcode Python题解之第386题字典序排数

题目&#xff1a; 题解&#xff1a; class Solution:def lexicalOrder(self, n: int) -> List[int]:ans [0] * nnum 1for i in range(n):ans[i] numif num * 10 < n:num * 10else:while num % 10 9 or num 1 > n:num // 10num 1return ans

太速科技-1路万兆光纤SFP+和1路千兆网络 FMC子卡模块

1路万兆光纤SFP和1路千兆网络 FMC子卡模块 一、概述 该板卡是基于kc705和ml605的fmc 10g万兆光纤扩展板设计&#xff0c;提供了1路万兆光纤SFP和1路千兆网络接口。可搭配我公司开发的FPGA载卡使用。载卡可参考&#xff1a;ID204 SFP&#xff08;10 Gigabit Small…

1-10 图像增强对比度 opencv树莓派4B 入门系列笔记

目录 一、提前准备 二、代码详解 enhanced_image cv2.convertScaleAbs(image, alpha1.5, beta0) 三、运行现象 四、完整工程贴出 一、提前准备 1、树莓派4B 及 64位系统 2、提前安装opencv库 以及 numpy库 3、保存一张图片 二、代码详解 import cv2 # 增强图像的对比度 …

【Arm Cortex-X925】 -【第五章】-电源管理

5. 电源管理 Cortex-X925 核心提供了控制动态和静态功耗的机制。 动态功耗管理包括以下特性: 层次时钟门控每核心的动态电压和频率调整 (DVFS)静态功耗管理包括以下特性: 关机模式动态保持,一种低功耗模式,能够保留寄存器和 RAM 状态5.1 电压和功率域 DynamIQ™ Shared …

代码随想录Day 32|leetcode题目:501.斐波那契数、70.爬楼梯、746.使用最小花费爬楼梯

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 动态规划理论基础一、理论基础1.1 什么是动态规划1.2 动态规划的解题步骤1.3 动态规划应该如何debug 二、题目题目一&#xff1a; 509. 斐波那契数解题思路&#xff1a;动态规划递归解法 题目二&a…

【OpenWrt(3)】内网搭建iperf3测速服务器

下载的iperf3 网站&#xff1a;https://iperf.fr/iperf-download.php Window地址&#xff1a;https://github.com/ar51an/iperf3-win-builds 安卓&#xff1a;https://gitee.com/hiyanyx/magic-i-perf 文章目录 下载的iperf3Windows 服务器启动安卓客户端启动参考 Windows 服务…

363_C++_配合360_负责读取和处理录像数据RecordReader类

其中的变量们: 读取器未启用 (!m_bEnReader) 已经有一个读取操作正在进行 (m_bPending) 读取器还未启动 (!m_bStarted) lastRealBytes:计算这帧数据实际需要的总字节数(包括未处理的部分和对齐的填充字节) mLastOffset:表示上次处理数据时的偏移位置 lastRemain 计算出…

电路基础笔记 --- 第一章

关于电路吸收及发出功率的判断&#xff1a; 其实关于这个问题根据实际电流和电压更好判断&#xff0c;我们根据参考电压及参考电流再结合各自数值画出对于元件来说的实际电流方向和电位高低&#xff0c;在实际电流方向通过元件时如果电位变高则代表元件在产生功率&#xff0c;…

【Linux】进程控制(一)

1. 进程创建 &#xff08;一&#xff09;认识fork函数 从已存在进程中创建一个新进程&#xff08;新进程为子进程&#xff0c;而原进程为父进程&#xff09; 进程调用fork&#xff0c;当控制转移到内核中的fork代码后&#xff0c;内核做&#xff1a; 分配新的内存块和内核数…