当前位置: 首页 > news >正文

【Nova UI】十六、打造组件库之滚动条组件(中):探秘滑块的计算逻辑

序言

在上篇文章中,我们完成了滚动条组件开发的前期准备工作,包括理论推导、布局规划和基础设置。现在,我们将把这些准备转化为实际代码,开启滚动条组件的具体开发之旅🌟。我们会详细阐述如何实现各项功能,解决开发中的技术挑战,为用户带来更好的滚动体验🌐。

滚动条

在设计滚动条组件时,我们需要考虑横向和纵向两个方向的滚动条。这两个方向的滚动条在功能实现和表现形式上有诸多相似之处,但也存在一些细微的差异。为了提高代码的可维护性、可复用性和可扩展性,我们决定将滚动条单独提取出来,作为一个独立的组件进行开发。基于此,我们在 packages/components/scrollbar/src 目录下创建了 bar.ts 和 bar.vue 文件,它们将作为实现滚动条功能的核心文件,为后续的开发工作提供强有力的支撑 🏗️。

bar.ts

import { ExtractPropTypes } from 'vue'
import { useDirectionProp } from '@nova-ui/hooks'
import type bar from './bar.vue'export const barProps = {direction: useDirectionProp(),always: Boolean,
} as constexport type BarType = ExtractPropTypes<typeof barProps>export type BarInstance = InstanceType<typeof bar>

在 bar.ts 文件中,barProps 包含 direction 和 always 两个属性。direction 用于确定滚动条的方向,并且可以在其他组件中复用 🔄。always 这个属性用于控制滚动条是否始终显示,以此来满足不同的使用场景 🔧。通过 InstanceType<typeof bar> 导出的 BarInstance 类型,有助于在操作滚动条组件实例时确保类型的准确性,避免出现类型错误,而且会在 scrollbar 组件中使用 🌟。

bar.vue

<template><divv-show="visible"ref="barRef":class="[n.b(),n.m(direction),n.is('always', always)]"><divref="thumbRef":class="n.e('thumb')"></div></div>
</template><script lang="ts" setup>
const n = useNamespace('bar')
defineOptions({name: 'NBar',
})
const props = defineProps(barProps)
</script>

bar.vue 的基本 DOM 结构由两个 div 元素组成,一个作为轨道,另一个作为滑块。首先,我们会定义以下几个重要的变量:

  • size:用于存储滑块的长或宽,方便后续的样式调整 📏。
  • ratio:表示视图区域与可视区域的比例,这是计算滑块大小的关键数据 🔢。
  • visible:决定滚动条是否显示,会根据不同的情况进行调整 👁️。
  • distance:表示滑块滚动的距离,它会根据用户的操作而变化呢 📏。

滑块大小计算

我们需要获取视图区域和可视区域的 DOM 元素。在 scrollbar 组件中通过 provide 传递,在 bar 组件中通过 inject 获取,以下是以纵向滚动条为例的代码,完整代码可查看相应的仓库 🔍。

const scrollbar = inject(scrollbarInjectionKey)
const visible = ref(false)
const size = ref(0)
const ratio = ref(1)const updateHandler = () => {const wrap = scrollbar?.wrapElementif (!wrap) returnconst { offsetHeight, scrollHeight }  = wrapconst _ratio = offsetHeight / scrollHeightconst height = _ratio * offsetHeightsize.value = heightratio.value = _ratiovisible.value = offsetHeight < scrollHeight
}

通过上述计算,我们可以得到所需的数值,进而为滑块添加合适的样式 🌟。

const thumbStyle = computed(() => {return {[props.direction === 'vertical' ? 'height' : 'width']: size.value ? addUnit(size.value) : undefined} 
})

滑块滚动距离

在完成滑块大小的计算和相关属性的获取之后,我们要计算滑块滚动的距离 🔢。

const scrollHandler = () => {const wrap = scrollbar?.wrapElementif (!wrap) returndistance.value = wrap.scrollTop * ratio.value
}

这个计算很简单,就是将视图区域滚动的距离与比例相乘。根据这个结果,我们将修改滑块的样式:

const thumbStyle = computed(() => {return {[props.direction === 'vertical' ? 'height' : 'width']: size.value ? addUnit(size.value) : undefined,transform: `translate${ props.direction === 'vertical' ? 'Y' : 'X' }(${ distance.value }px)`}
})

至此,基本功能已成型 👏。接下来,我们来看看如何触发这些方法。

scrollbar 更新 bar的 滑块

在 VNode 更新之后,我们需要调用相应的方法,这里使用 onUpdated 生命周期来完成。为了更好地兼容不同的场景,当视图区域大小发生变化时,我们会使用 useResizeObserver 来监听 DOM 变化并调用 updateHandler 。

const update = () => {barUpdateHandler()barScrollHandler()
}let stopResizeObserver: (() => void) | undefined = undefined
watch(() => props.noresize, (noresize) => {if (noresize) {stopResizeObserver?.()} else {const { stop } = useResizeObserver(wrapRef as unknown as MaybeComputedElementRef, update)stopResizeObserver = stop}
}, { immediate: true })onUpdated(() => {update()
})

scrollbar 更新 bar的 滚动距离

在设置好滑块的大小之后,我们需要处理滚动距离。我们会对滚动区域进行监听,触发 bar 的相关函数。

const barScrollHandler = () => {barVerticalRef.value?.scroll()barHorizontalRef.value?.scroll()
}
const onScroll = () => {barScrollHandler()emits('scroll', {scrollTop: wrapRef.value?.scrollTop || 0,scrollLeft: wrapRef.value?.scrollLeft || 0,})
}

🦀🦀感谢看官看到这里,如果觉得文章不错的话🙌,点个关注不迷路⭐。
诚邀您加入我的微信技术交流群🎉,群里都是志同道合的开发者👨‍💻,大家能一起交流分享摸鱼🐟。期待与您在群里相见🚀,咱们携手在开发路上共同进步✨ !
👉点我

感谢各位大侠一路相伴,实在感激! 不瞒您说,在下还有几个开源项目 📦,它们就像精心培育的幼苗 🌱,急需您的浇灌。要是您瞧着还不错,麻烦动动手指,给它们点亮几颗 Star ⭐,您的支持就是它们成长的最大动力,在此谢过各位大侠啦!

  • Nova UI组件库:https://github.com/gmingchen/nova-ui
  • 基于 Vue3 + Element-plus 管理后台基础功能框架
  • 预览:https://admin.gumingchen.icu
    • Github:https://github.com/gmingchen/agile-admin
    • Gitee:https://gitee.com/shychen/agile-admin
    • 基础版后端:https://github.com/gmingchen/java-spring-boot-admin
    • 文档:http://admin.gumingchen.icu/doc/
  • 基于 Vue3 + Element-plus + websocket 即时聊天系统
    • 预览:https://chatterbox.gumingchen.icu/
    • Github:https://github.com/gmingchen/chatterbox
    • Gitee:https://gitee.com/shychen/chatterbox
  • 基于 node 开发的后端服务:https://github.com/gmingchen/node-server

相关文章:

  • GNSS数据自动化下载系统的设计与实现
  • 互信息与KL散度:差异与应用全解析
  • Linux系统:文件系统前言,详解CHSLBA地址
  • 自适应稀疏核卷积网络:一种高效灵活的图像处理方案
  • iOS - 如何从appStore获取app版本信息
  • 电能质量扰动信号信号通过hilbert变换得到瞬时频率
  • 第五部分:第一节 - Node.js 简介与环境:让 JavaScript 走进厨房
  • 如何优化MCU中断响应时间
  • 【AI面试秘籍】| 第7期:多轮对话如何实现长期记忆?高频考点解析+代码实战
  • 使用腾讯会议远程控制电脑进行操作电脑
  • C语言中的assert
  • 一种基于光源评估并加权平均的自动白平衡方法(二)
  • OpenCV中的光流估计方法详解
  • Kaamel隐私合规洞察:Facebook美容定向广告事件分析
  • 不用联网不用编程,PLC通过智能网关快速实现HTTP协议JSON格式与MES等系统平台双向数据通讯
  • 使用WebStorm打断点调试Vue项目
  • 2025-05-13 学习记录--Python-数据类型转换 + 运算符
  • 乙酰基六肽-39/Silusyne 新型减肥活性肽,减少脂肪堆积
  • 开源免费无广告专注PDF编辑、修复和管理工具 办公学术 救星工具
  • 【完全平方数包含相同数】2021-11-30
  • 中国-拉共体论坛第四届部长级会议北京宣言
  • 周启鸣加盟同济大学,曾任香港浸会大学深圳研究院院长
  • 济南市委副秘书长吕英伟已任历下区领导
  • 老人将房产遗赠给外孙,三个女儿却认为遗嘱应无效,法院判了
  • 母亲节书单|关于生育自由的未来
  • 王毅同巴基斯坦副总理兼外长达尔通电话