html+css
<template>
<div class="wrap" :style="style">
<div>
<div
v-for="(item,i) in list"
:key="i"
@click="autoScroll(item.top, item.id)"
:class="{item: item,active: item.active}"
>
{{item.title}}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
style: '',
list: [
{
title: 'demo1',
active: true,
id: 'title-1',
},
{
title: 'demo2',
active: false,
id: 'title-2',
},
{
title: 'demo3',
active: false,
id: 'title-3',
},
],
}
},
// 将需要的跳转的组件引入
computed: {
...mapState('merchant/register', ['demo1', 'demo2', 'demo3']),
},
<style>
.wrap {
position: fixed;
top: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
}
.item {
position: relative;
padding: 14px 0px 14px 24px;
min-width: 130px;
font-size: 14px;
line-height: 14px;
color: #fff;
cursor: pointer;
}
.active {
color: rgb(50, 90, 180);
&:after {
content: ' ';
position: absolute;
left: 0px;
top: 20px;
height: 2px;
width: 12px;
background-color: rgb(50, 90, 180);
}
}
</style>
没有全局监听滚动,单纯的点击滚动, 跨页面
methods: {
// 点击锚点进行滚动
autoScroll(id) {
// 点击的时候获取当前元素的offsetTop值
const el = document.querySelector('#' + id)
const top = el.offsetTop
try {
// 返回文档在垂直方向已滚动的像素值。
let curY = window.scrollY
let STEP = Math.abs(curY - top) / 40
if (STEP < 10) STEP = 10
const f = () => {
if (top == null || curY == null) return
let delt = Math.abs(curY - top)
if (!delt) return
if (delt <= STEP) curY = top
else curY = curY + (curY > top ? -STEP : STEP)
window.scrollTo(0, curY)
setTimeout(f, 0)
}
f()
} catch (e) {
console.log(e)
}
// deep copy
const list = [...this.list.map((v) => ({ ...v, active: false }))]
list.forEach((v) => {
if (v.id === id) {
v.active = true
return true
}
return false
})
this.list = list
},
},
mounted() {
const width = (document.body.clientWidth - 1200) / 2 - 120
this.style = `left: ${width}px`
},
}
全局监听滚动, 跨页面
使用的IntersectionObserver 方法
具体使用方法参照阮大的文章, 点击即可进入
methods: {
// 初始化 快捷链接位置 和 锚点 坐标,当list发生改变时需要调用
initPosition() {
try {
if (typeof window === 'undefined') return
const width = (document.body.clientWidth - 1200) / 2 - 130
this.style = `left: ${width}px`
const anchorList = this.list.map((item) => {
const el = document.querySelector('#' + item.id)
return {
...item,
top: el.offsetTop,
}
})
this.list = anchorList
} catch (e) {
console.log(e)
}
},
async observerHandle(entrys) {
// deep copy
const anchorList = [
...this.list.map((v) => ({ ...v, active: false })),
]
entrys.forEach((v) => {
let index = +v.target.id.split('title-')[1] - 1
anchorList[index].show =
v.intersectionRatio > 0 && v.intersectionRect.bottom >= 100
})
// 只活跃第一个显示在视口的title
anchorList.some((v) => {
if (v.show) {
v.active = true
return true
}
return false
})
this.list = anchorList
},
intersectionObserverInit() {
// 停止监听工作
this.io && this.io.disconnect()
const thresholds = []
for (let i = 0; i <= 1; i += 0.01) thresholds.push(i)
this.io = new IntersectionObserver(this.observerHandle, {
threshold: thresholds,
})
this.list.length &&
this.list.forEach(({ id }) => {
const el = document.querySelector('#' + id)
// 监听
el && this.io.observe(el)
})
},
// 点击锚点进行滚动
autoScroll(id) {
try {
// 返回文档在垂直方向已滚动的像素值。
let curY = window.scrollY
let STEP = Math.abs(curY - top) / 40
if (STEP < 10) STEP = 10
const f = () => {
if (top == null || curY == null) return
let delt = Math.abs(curY - top)
if (!delt) return
if (delt <= STEP) curY = top
else curY = curY + (curY > top ? -STEP : STEP)
window.scrollTo(0, curY)
setTimeout(f, 0)
}
f()
} catch (e) {
console.log(e)
}
},
},
mounted() {
this.$nextTick(() => {
this.initPosition()
this.intersectionObserverInit()
})
},
}
本文介绍了如何在Vue中实现滚动效果,包括不监听全局滚动的点击滚动和全局监听滚动的跨页面滚动。主要利用IntersectionObserver方法,详细步骤可参考阮一峰的文章。

1073

被折叠的 条评论
为什么被折叠?



