vue3 h5移动端滚动工具函数

485 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

前言

新的一年,开启新的篇章,第一篇写一写工作中实用的函数封装方法,继续努力,相互进步!

一. 前置知识

我们先复习一下什么是clientHeightoffsetHeightscrollHeightscrollTop

1.clientHeight

返回元素内部的高度(单位像素),包含内边距,不包括水平滚动条、边框和外边距。 此属性会将获取的值四舍五入取整数

clientHeight = CSS height + CSS padding - 水平滚动条高度 (如果存在)

image.png

2.offsetHeight

它返回该元素的像素高度,高度包含该元素的垂直内边距和边框和元素的水平滚动条(如果存在且渲染的话),不包含:before:after 等伪类元素的高度。且是一个整数。

image.png

上面的图片中显示了 scollbar 和窗口高度的 offsetHeight.但是不能滚动的元素可能会有一个很大的高度值,大于可以看见的内容。这些元素原则上是被包含在滚动元素之中的。所以,这些不能滚动的元素可能会因为 scrollTop 的值会被完全隐藏或者部分隐藏;

3.scrollHeight

一个元素内容高度的度量,包括由于溢出导致的视图中不可见内容。

image.png

scrollHeight 的值等于该元素在不使用滚动条的情况下为了适应视口中所用内容所需的最小高度。没有垂直滚动条的情况下,scrollHeight 值与元素视图填充所有内容所需要的最小值clientHeight相同。包括元素的 padding,但不包括元素的 bordermarginscrollHeight 也包括 ::before 和 ::after这样的伪元素。 如果元素的内容不需要垂直滚动条就可以容纳,则其 scrollHeight 等于{{domref("Element.clientHeight", "clientHeight")}}

4.scrollTop

元素滚动到屏幕上面不可见区域高度。当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为0。

判断滑动是否触底:clientHeight+ scrollTop >= scrollHeight 或Math.abs(element.scrollHeight - element.clientHeight - element.scrollTop) < 1

贴个整体图: image.png

二. 工具函数

安装方式: 具体代码实现方式可在npm官网搜索即可。

 yarn add hjh-scroll

情况1:判断该页面是否触底,获取对应参数(注意是整个页面

应用场景举例:通过scrollTop滑动距离展示内容是否显示,滑动到底部加载新的内容

  <div class="search-bar" v-if="isShowSearchBar">
      <search-bar/>
  </div>
import { useScroll } from "hjh-scroll";
const { isReachBottom, scrollTop } = useScroll()

// 定义的可响应式数据, 依赖另外一个可响应式的数据, 那么可以使用计算函数(computed)
const isShowSearchBar = computed(() => {
  return scrollTop.value >= 100
})

watch(isReachBottom, (newValue) => {
  if (newValue) {
  //获取数据最好写在store中
    mianStore.getlistData().then(() => {
      isReachBottom.value = false
    })
  }
})

情况2:判断该元素滑动是否触底,获取对应参数

应用场景举例:详情页tabControl,点击对应tab,滑动到指定tab对应的位置

<template>
  <div class="detail top-page" ref="detailRef">
    <tab-control
      v-if="showTabControl"
      class="tabs"
      :titles="names"
      @tabItemClick="tabClick"
    />


    <div class="main" v-if="mainPart" v-memo="[mainPart]">
      <detail-swipe :swipe-data="mainPart.xxx"/>
      <detail-infos name="xxx" :ref="getSectionRef" :top-infos="mainPart.xxx"/>
    </div>
  </div>
</template>

v-memo: 数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过

// 发送网络请求获取数据
const detailInfos = ref({})
const mainPart = computed(() => detailInfos.value.mainPart)
getDetailInfos(houseId).then(res => {
  detailInfos.value = res.data
})
// tabControl相关的操作
const detailRef = ref()
const { scrollTop } = useScroll(detailRef)
const showTabControl = computed(() => {
  return scrollTop.value >= 300
})

const sectionEls = ref({})
const names = computed(() => {
  return Object.keys(sectionEls.value)
})
const getSectionRef = (value) => {
  const name = value.$el.getAttribute("name")
  sectionEls.value[name] = value.$el
}
const tabClick = (index) => {
  const key = Object.keys(sectionEls.value)[index]
  const el = sectionEls.value[key]
  let instance = el.offsetTop
  if (index !== 0) {
    instance = instance - 44
  }

  detailRef.value.scrollTo({
    top: instance,
    behavior: "smooth"
  })
}