Skip to content

滚动组件 Scrollbar

由于默认浏览器滚动条极为丑陋切不同浏览器之间样式不统一,故封装一个滚动组件用于实现滚动

基础用法

使用b-scrollbar进行包裹,默认slot为内容显示区域,如当前示例所包含的滚动结构如下:

滚动示例块 1
滚动示例块 2
滚动示例块 3
滚动示例块 4
滚动示例块 5
滚动示例块 6
滚动示例块 7
滚动示例块 8
滚动示例块 9
滚动示例块 10
滚动示例块 11
滚动示例块 12
<template>
  <div style="height: 300px">
    <b-scrollbar ref="componentScrollBar" style="height: 100%">
      <div class="scrollbar-demo-list">
        <div v-for="i in 12" :key="i" class="scrollbar-demo-item">滚动示例块 {{ i }}</div>
      </div>
    </b-scrollbar>
  </div>
</template>

<style scoped>
.scrollbar-demo-list {
  padding: 4px;
}

.scrollbar-demo-item {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 56px;
  margin: 10px 0;
  border-radius: 8px;
  background: linear-gradient(135deg, #f8fbff 0%, #eef5ff 100%);
  color: #3a4a66;
  font-weight: 500;
}
</style>

注意:如果内容区域不超过容器高度则不会生成滚动条

最大高度

通过 max-height 可以限制滚动区域的最大高度,只有内容超出时才会出现滚动条。

第 1 项内容

第 2 项内容

第 3 项内容

第 4 项内容

第 5 项内容

<template>
  <div>
    <div style="margin-bottom: 12px">
      <b-button size="small" @click="add">增加内容</b-button>
      <b-button size="small" style="margin-left: 8px" @click="remove">删除内容</b-button>
    </div>
    <b-scrollbar max-height="320px">
      <p v-for="item in count" :key="item" class="scrollbar-demo-item">
{{ item }} 项内容
      </p>
    </b-scrollbar>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const count = ref(5)

function add() {
  count.value++
}

function remove() {
  if (count.value > 0) {
    count.value--
  }
}
</script>

<style scoped>
.scrollbar-demo-item {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 48px;
  margin: 10px 0;
  border-radius: 6px;
  background: #f5f7fa;
  color: #606266;
}
</style>

手动滚动

可以通过组件暴露的 setScrollTopsetScrollLeft 方法手动控制滚动位置。

手动滚动示例 1

手动滚动示例 2

手动滚动示例 3

手动滚动示例 4

手动滚动示例 5

手动滚动示例 6

手动滚动示例 7

手动滚动示例 8

手动滚动示例 9

手动滚动示例 10

手动滚动示例 11

手动滚动示例 12

手动滚动示例 13

手动滚动示例 14

手动滚动示例 15

手动滚动示例 16

手动滚动示例 17

手动滚动示例 18

手动滚动示例 19

手动滚动示例 20

<template>
  <div>
    <b-scrollbar ref="scrollbarRef" height="320px" always @scroll="handleScroll">
      <div ref="innerRef">
        <p v-for="item in 20" :key="item" class="scrollbar-demo-item">
          手动滚动示例 {{ item }}
        </p>
      </div>
    </b-scrollbar>

    <div style="margin-top: 16px">
      <b-slider v-model="value" :max="max" :format-tooltip="formatTooltip"></b-slider>
    </div>
  </div>
</template>

<script setup lang="ts">
import { nextTick, onMounted, ref, watch } from 'vue'

const max = ref(0)
const value = ref(0)
const innerRef = ref<HTMLElement | null>(null)
const scrollbarRef = ref<{
  setScrollTop: (scrollTop: number) => void
  wrapRef?: {
    value: HTMLElement | null
  }
} | null>(null)

function handleScroll({ scrollTop }: { scrollTop: number }) {
  value.value = scrollTop
}

function formatTooltip(currentValue: number) {
  return `${currentValue}px`
}

function updateMaxScroll() {
  const wrapElement = scrollbarRef.value?.wrapRef?.value
  const contentHeight = innerRef.value?.clientHeight || 0
  const viewportHeight = wrapElement?.clientHeight || 0

  max.value = Math.max(contentHeight - viewportHeight, 0)
}

watch(value, currentValue => {
  scrollbarRef.value?.setScrollTop(currentValue)
})

onMounted(async () => {
  await nextTick()
  updateMaxScroll()
})
</script>

<style scoped>
.scrollbar-demo-item {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 48px;
  margin: 10px 0;
  border-radius: 6px;
  background: #f5f7fa;
  color: #606266;
}
</style>

始终显示

使用always可以让滚动条始终显示出来

始终显示滚动块 1
始终显示滚动块 2
始终显示滚动块 3
始终显示滚动块 4
始终显示滚动块 5
始终显示滚动块 6
始终显示滚动块 7
始终显示滚动块 8
始终显示滚动块 9
始终显示滚动块 10
始终显示滚动块 11
始终显示滚动块 12
<template>
  <div style="height: 300px">
    <b-scrollbar ref="componentScrollBar" always noresize>
      <div class="scrollbar-demo-list">
        <div v-for="i in 12" :key="i" class="scrollbar-demo-item">始终显示滚动块 {{ i }}</div>
      </div>
    </b-scrollbar>
  </div>
</template>

<style scoped>
.scrollbar-demo-list {
  padding: 4px;
}

.scrollbar-demo-item {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 56px;
  margin: 10px 0;
  border-radius: 8px;
  background: linear-gradient(135deg, #f8fbff 0%, #eef5ff 100%);
  color: #3a4a66;
  font-weight: 500;
}
</style>

定制滚动条的样式

可以借助不同的props来定制滚动条的样式,也可以使用css来实现样式修改。

自定义样式滚动块 1
自定义样式滚动块 2
自定义样式滚动块 3
自定义样式滚动块 4
自定义样式滚动块 5
自定义样式滚动块 6
自定义样式滚动块 7
自定义样式滚动块 8
自定义样式滚动块 9
自定义样式滚动块 10
自定义样式滚动块 11
自定义样式滚动块 12
<template>
  <div style="height: 300px">
    <b-scrollbar
      ref="componentScrollBar"
      always
      noresize
      :bar-style="{ background: 'rgba(110, 23, 122, 0.3)' }"
      :bar-wrap-style="{ background: 'rgba(0, 0, 0, 0.03)' }"
    >
      <div class="scrollbar-demo-list">
        <div v-for="i in 12" :key="i" class="scrollbar-demo-item">自定义样式滚动块 {{ i }}</div>
      </div>
    </b-scrollbar>
  </div>
</template>

<style scoped>
.scrollbar-demo-list {
  padding: 4px;
}

.scrollbar-demo-item {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 56px;
  margin: 10px 0;
  border-radius: 8px;
  background: linear-gradient(135deg, #f7efff 0%, #efe4ff 100%);
  color: #5c2a86;
  font-weight: 500;
}
</style>

无限滚动

滚动到边缘时会触发 end-reached 事件,事件参数支持 top / bottom / left / right 四个方向,常用于无限加载。

无限滚动内容 1

无限滚动内容 2

无限滚动内容 3

无限滚动内容 4

无限滚动内容 5

无限滚动内容 6

无限滚动内容 7

无限滚动内容 8

无限滚动内容 9

无限滚动内容 10

无限滚动内容 11

无限滚动内容 12

无限滚动内容 13

无限滚动内容 14

无限滚动内容 15

无限滚动内容 16

无限滚动内容 17

无限滚动内容 18

无限滚动内容 19

无限滚动内容 20

<template>
  <b-scrollbar height="320px" :distance="16" @end-reached="loadMore">
    <p v-for="item in count" :key="item" class="scrollbar-demo-item">
      无限滚动内容 {{ item }}
    </p>
  </b-scrollbar>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const count = ref(20)

function loadMore(direction: 'top' | 'bottom' | 'left' | 'right') {
  if (direction === 'bottom') {
    count.value += 5
  }
}
</script>

<style scoped>
.scrollbar-demo-item {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 48px;
  margin: 10px 0;
  border-radius: 6px;
  background: #f5f7fa;
  color: #606266;
}
</style>

注意事项

  • b-scrollbar的父层要有固定高度
  • b-scrollbar的高度要设成100%
  • 如果出现横滚动条,请添加css(.bin-scrollbar__wrap{overflow-x:hidden;})

Props

参数说明类型可选值默认值
distance触发 end-reached 的距离阈值,单位 pxNumber-0
height滚动区域高度String / Number-
maxHeight滚动区域最大高度String / Number-
native是否采用原生滚动 (隐藏原生滚动条)Booleantruefalse
always是否一直显示,而非悬停显示Booleantruefalse
wrapStyle内联方式自定义 wrap 容器样式String / Object / Array-
wrapClass类名方式自定义 wrap 容器样式String / Array-
viewClass类名方式自定义 view 容器样式String / Array-
viewStyle内联方式自定义 view 容器样式String / Object / Array-
noresize如果 container 尺寸不会发生变化,最好设置它可以优化性能Booleantruefalse
tag视图容器元素标签String-div
minSize滚动条最小尺寸Number-20
tabindexwrap 容器的 tabindexString / Number-
barStyle滚动条 thumb 样式Object-{}
barWrapStyle滚动条 bar 容器样式Object-{}

Events

事件名称说明回调参数
scroll滚动时触发{ scrollTop, scrollLeft }
end-reached滚动触达边界时触发direction: 'top' | 'bottom' | 'left' | 'right'

Methods

方法名说明参数
handleScroll手动触发一次滚动处理
scrollTo滚动到指定位置(options)(x, y)
setScrollTop设置纵向滚动距离(scrollTop: number)
setScrollLeft设置横向滚动距离(scrollLeft: number)
update手动更新滚动条状态
wrapRef滚动容器实例引用