前言
在业务代码中,我们经常会遇见这样的需求,头部有一个导航,页面上滑过程中,头部导航会吸附到固定区域,并且其显示的内容会随着滚动范围的变化而变化。
头部悬停.png
mand-mobile的引入
这里我们使用vue-cli
创建项目,创建完毕后在cmd
中输入:
npm install mand-mobile --save
为了简单叙述,我们使用全量引入,在main.js
中加入:
import mandMobile from 'mand-mobile'
import 'mand-mobile/lib/mand-mobile.css'
Vue.use(mandMobile)
stylus的引入
由于样式使用的是stylus
预处理器,因此我们要引入stylus
,在cmd中输入:
cnpm install stylus --save
cnpm install stylus-loader css-loader style-loader --save-dev
代码思路
先对这个需求进行分析,显然,头部导航的内容和滚动是进行联动的,因此要判断当前滚动的范围属于哪一个区域。下面通过视图对思路进行一个分析:
图解.png
首先,我们需要得到每一个scroll-view-category
容器,放到一个blocks
数组中。然后计算其offsetTop
和offsetBottom
,将其存入dimensions
数组中。在滚动时,只需要判断当前的scrollY
在哪一个容器的高度范围内即可。
代码(有注释)
<template>
<div class="md-example-child md-example-child-scroll-view md-example-child-scroll-view-4">
<md-scroll-view
ref="scrollView"
:scrolling-x="false"
@scroll="$_onScroll"
>
<div
v-for="i in category"
:key="i"
class="scroll-view-category"
>
<p class="scroll-view-category-title">{{ i }}</p>
<div
v-for="j in list"
:key="j"
class="scroll-view-list"
>
<p class="scroll-view-item">{{`${i} - ${j}`}}</p>
</div>
</div>
</md-scroll-view>
<p v-if="activeBlockIndex > -1" class="scroll-view-striky-title">{{ category[activeBlockIndex] }}</p>
</div>
</template>
<script>
import {ScrollView} from 'mand-mobile'
export default {
name: 'stickyTitle',
components: {
[ScrollView.name]: ScrollView,
},
data() {
return {
category: [
'2020-06-08 周一',
'2020-06-05 周五',
'2020-06-04 周四',
],
list: [
'我是2020-06-08的内容',
'我是2020-06-05的内容',
'我是2020-06-04的内容',
],
dimensions: [],
scrollY: 0,
}
},
computed: {
activeBlockIndex() {
let activeIndex = -1
this.dimensions.forEach((dimension, index) => {
// dimension[0]: offset && dimension[1]: offsetBottom
if (this.scrollY >= dimension[0] && this.scrollY <= dimension[1]) {
// activeIndex = index + 1
activeIndex = index
}
})
return activeIndex
},
},
mounted() {
// 如果内容发生变化,需重新初始化block和scroller
this.$_initScrollBlock()
// this.$refs.scrollView.reflowScroller()
},
methods: {
$_initScrollBlock() {
const blocks = this.$el.querySelectorAll('.scroll-view-category')
let offset = 0
// blocks.forEach((block, index) => {
// 先把blocks转换成真正的数组,再执行blocks.slice
Array.prototype.slice.call(blocks).forEach((block, index) => {
// 计算每一个'.scroll-view-category'的高度
const innerHeight = block.clientHeight
// this.$set(Object, key, value)
// 给每一个'.scroll-view-category'添加一个offsetTop和一个offsetBottom,方便后期进行判断
this.$set(this.dimensions, index, [offset, offset + innerHeight])
offset += innerHeight
})
},
$_onScroll({scrollTop}) {
this.scrollY = scrollTop
},
},
}
</script>
<style lang="stylus" scoped>
.md-example-child-scroll-view-4
position relative
height 800px
background #FFF
.scroll-view-striky-title
position absolute
top 0
left 0
right 0
z-index 2
.scroll-view-category-title, .scroll-view-striky-title
padding 10px 0
text-align center
font-size 32px
font-family DINAlternate-Bold
background-color #f0f0f0
.scroll-view-item
padding 30px 0
text-align center
font-size 32px
border-bottom .5px solid #efefef
</style>