要求:
1. 顶部标题滚动,点击标题自动滚动到中间
2. 点击标题,定位到特定内容
3. 内容滚动,标题跟随
某大佬文章内容 uni-app scroll-view 点击实现元素居中?_uni-app vue3 scrollview getscrollw-CSDN博客
思路:使用scroll-view scroll-left (设置横向滚动条的位置,也可以使用 scroll-into-view,但是有固定标题时,滚上去的内容总是被遮挡一部分 ) scroll-with-animation(设置过渡动画)
<template>
<view style="">
<image src="https://img2.baidu.com/it/u=4017416575,2916339385&fm=253&fmt=auto&app=138&f=JPEG?w=681&h=360"
style="width: 100%;vertical-align: bottom;" mode="widthFix"></image>
<u-sticky> <!--uview 吸顶组件 就不用自己写固定定位了-->
<scroll-view :scroll-x="true" class="tab-title" :scroll-left="scrollLeft" scroll-with-animation>
<view :class="{'tab-title-item':true,'active':type===index}" @click="switchType(index)"
v-for="(item,index) in titleList" :key="index">
{{item.label}}
</view>
</scroll-view>
</u-sticky>
<view>
<view v-for="(val,index) in pinleiList" :key="index" :id="'target'+index" class="goods-content-item">
<view class="title ">
{{val.label}}
</view>
<view class="item-content" :style="{'background-color':val.bgc}">
<view class="goods">
<view class="goods-item" v-for="(item,index) in val.goods" :key="index">
<view style="position: relative;">
<u-lazy-load threshold="-300" border-radius="10" :image="item.imgurl" :index="index"></u-lazy-load>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<template>
<view style="">
<image src="https://img2.baidu.com/it/u=4017416575,2916339385&fm=253&fmt=auto&app=138&f=JPEG?w=681&h=360"
style="width: 100%;vertical-align: bottom;" mode="widthFix"></image>
<u-sticky>
<scroll-view :scroll-x="true" class="tab-title" :scroll-left="scrollLeft" scroll-with-animation>
<view :class="{'tab-title-item':true,'active':type===index}" @click="switchType(index)"
v-for="(item,index) in titleList" :key="index">
{{item.label}}
</view>
</scroll-view>
</u-sticky>
<view>
<view v-for="(val,index) in pinleiList" :key="index" :id="'target'+index" class="goods-content-item">
<view class="title ">
{{val.label}}
</view>
<view class="item-content" :style="{'background-color':val.bgc}">
<view class="goods">
<view class="goods-item" v-for="(item,index) in val.goods" :key="index">
<view style="position: relative;">
<u-lazy-load threshold="-300" border-radius="10" :image="item.imgurl" :index="index"></u-lazy-load>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const pinleiList = [{
label: "风景1",
bgc: '#FF8C50', // 默认背景色,可以不写,除非需要改颜色
goods: [ //商品
{
imgurl: 'https://img0.baidu.com/it/u=631814769,3587830013&fm=253&fmt=auto?w=200&h=200'
},
{
imgurl: 'https://img0.baidu.com/it/u=631814769,3587830013&fm=253&fmt=auto?w=200&h=200'
},
{
imgurl: 'https://img0.baidu.com/it/u=631814769,3587830013&fm=253&fmt=auto?w=200&h=200'
},
{
imgurl: 'https://img0.baidu.com/it/u=631814769,3587830013&fm=253&fmt=auto?w=200&h=200'
}
]
},
{
label: "风景2",
bgc: '#50d8ff',
goods: [ //商品
{
imgurl: 'https://img1.baidu.com/it/u=1048813378,1100765611&fm=253&fmt=auto&app=138&f=JPEG?w=190&h=190'
},
{
imgurl: 'https://img1.baidu.com/it/u=1048813378,1100765611&fm=253&fmt=auto&app=138&f=JPEG?w=190&h=190'
},
{
imgurl: 'https://img1.baidu.com/it/u=1048813378,1100765611&fm=253&fmt=auto&app=138&f=JPEG?w=190&h=190'
},
{
imgurl: 'https://img1.baidu.com/it/u=1048813378,1100765611&fm=253&fmt=auto&app=138&f=JPEG?w=190&h=190'
}
]
},
{
label: "风景3",
bgc: '#FF8C50',
goods: [ //商品
{
imgurl: 'https://img1.baidu.com/it/u=4253149046,3580800107&fm=253&fmt=auto&app=120&f=JPEG?w=200&h=200'
},
{
imgurl: 'https://img1.baidu.com/it/u=4253149046,3580800107&fm=253&fmt=auto&app=120&f=JPEG?w=200&h=200'
},
{
imgurl: 'https://img1.baidu.com/it/u=4253149046,3580800107&fm=253&fmt=auto&app=120&f=JPEG?w=200&h=200'
},
{
imgurl: 'https://img1.baidu.com/it/u=4253149046,3580800107&fm=253&fmt=auto&app=120&f=JPEG?w=200&h=200'
}
]
},
{
label: "风景4",
bgc: '#FF8C50',
goods: [ //商品
{
imgurl: 'https://img2.baidu.com/it/u=1762261007,2519826657&fm=253&fmt=auto&app=138&f=JPEG?w=200&h=200'
},
{
imgurl: 'https://img2.baidu.com/it/u=1762261007,2519826657&fm=253&fmt=auto&app=138&f=JPEG?w=200&h=200'
},
{
imgurl: 'https://img2.baidu.com/it/u=1762261007,2519826657&fm=253&fmt=auto&app=138&f=JPEG?w=200&h=200'
},
{
imgurl: 'https://img2.baidu.com/it/u=1762261007,2519826657&fm=253&fmt=auto&app=138&f=JPEG?w=200&h=200'
}
]
},
{
label: "风景5",
bgc: '#FF8C50',
goods: [ //商品
{
imgurl: 'https://img2.baidu.com/it/u=1084408498,2948431230&fm=253&fmt=auto&app=138&f=JPEG?w=130&h=170'
},
{
imgurl: 'https://img2.baidu.com/it/u=1084408498,2948431230&fm=253&fmt=auto&app=138&f=JPEG?w=130&h=170'
},
{
imgurl: 'https://img2.baidu.com/it/u=1084408498,2948431230&fm=253&fmt=auto&app=138&f=JPEG?w=130&h=170'
},
{
imgurl: 'https://img2.baidu.com/it/u=1084408498,2948431230&fm=253&fmt=auto&app=138&f=JPEG?w=130&h=170'
}
]
},
{
label: "风景6",
bgc: '#FF8C50',
goods: [ //商品
{
imgurl: 'https://img0.baidu.com/it/u=2414855935,1287564153&fm=253&fmt=auto&app=138&f=JPEG?w=200&h=200'
},
{
imgurl: 'https://img0.baidu.com/it/u=2414855935,1287564153&fm=253&fmt=auto&app=138&f=JPEG?w=200&h=200'
},
{
imgurl: 'https://img0.baidu.com/it/u=2414855935,1287564153&fm=253&fmt=auto&app=138&f=JPEG?w=200&h=200'
},
{
imgurl: 'https://img0.baidu.com/it/u=2414855935,1287564153&fm=253&fmt=auto&app=138&f=JPEG?w=200&h=200'
}
]
},
{
label: "风景7",
bgc: '#FF8C50',
goods: [ //商品
{
imgurl: 'https://img2.baidu.com/it/u=652299287,3144977570&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1372'
},
{
imgurl: 'https://img2.baidu.com/it/u=652299287,3144977570&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1372'
},
{
imgurl: 'https://img2.baidu.com/it/u=652299287,3144977570&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1372'
},
{
imgurl: 'https://img2.baidu.com/it/u=652299287,3144977570&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1372'
}
]
},
]
export default {
data() {
return {
pinleiList,
type: 0,
titleList: [],
contentScrollW: 0, // 导航区宽度
curIndex: 0, // 当前选中
scrollLeft: 0, // 横向滚动条位置
scrollTop: 0, // 滚动出去的距离
}
},
onReady() {
this.titleList = pinleiList.map(item => {
return {
label: item.label
}
})
this.$nextTick(() => {
this.getScrollW();
})
},
// 滚动条滚动触发
onPageScroll(e) {
// e.scrollTop 表示当前页面滚动的距离
this.scrollTop = e.scrollTop
this.updateTitle()
},
onLoad() {
// 进入页面就滚动到顶部
uni.pageScrollTo({
scrollTop: 0
});
},
methods: {
updateTitle() {
const query = uni.createSelectorQuery().in(this);
query.selectAll('.goods-content-item').boundingClientRect((data) => {
let dataLen = data.length;
for (let i = 0; i < dataLen; i++) {
// 找到此时在可视区域内的 部分 ,这里去除了固定栏高度 更新标题
if (data[i].top <= this.tabTitleHeight && data[i].bottom > 0) {
this.type = i;
this.scrollLeft = this.titleList[i].left - this.contentScrollW / 2 + this.titleList[i].width / 2;
}
}
}).exec();
},
getScrollW() {
const query = uni.createSelectorQuery().in(this);
query.select('.tab-title').boundingClientRect(data => {
// 拿到 scroll-view 组件宽度
this.contentScrollW = data.width
this.tabTitleHeight = data.height
}).exec();
query.selectAll('.tab-title-item').boundingClientRect(data => {
let dataLen = data.length;
for (let i = 0; i < dataLen; i++) {
// scroll-view 子元素组件距离左边栏的距离
this.titleList[i].left = data[i].left;
// scroll-view 子元素组件宽度
this.titleList[i].width = data[i].width
}
}).exec()
},
switchType(index) {
this.type = index;
this.scrollLeft = this.titleList[index].left - this.contentScrollW / 2 + this.titleList[index].width / 2;
this.scrollToTop()
},
// 滚动到顶部
scrollToTop() {
const query = uni.createSelectorQuery().in(this);
query.select('#target' + this.type).boundingClientRect(data => {
uni.pageScrollTo({
// 距离顶部的高度 + 已滚动高度 - 固定标题栏高度 (如果不减去标题栏高度,总是有一部分会被遮挡)
scrollTop: data.top + this.scrollTop - this.tabTitleHeight,
duration: 200
});
}).exec();
},
}
}
</script>
<style scoped>
.tab-title {
background: #F7F7F7;
white-space: nowrap;
padding: 0 30rpx;
box-sizing: border-box;
}
.tab-title-item {
display: inline-block;
height: 84rpx;
line-height: 84rpx;
font-weight: 400;
font-size: 35rpx;
color: #666666;
margin-right: 28rpx;
}
.tab-title-item.active {
border-bottom: 4rpx solid #EB1313;
color: #EB1313;
}
.title {
padding: 32rpx 18rpx;
font-weight: bold;
font-size: 33rpx;
color: #363636;
position: relative;
margin: 0 22rpx;
}
.title::before {
content: "";
width: 7rpx;
height: 51rpx;
background: #EB1313;
border-radius: 3rpx;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
}
.item-content {
margin: 0 22rpx;
background: #FF8C50;
border-radius: 10rpx;
padding: 16rpx 16rpx 0 16rpx;
}
.goods {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.goods-item {
width: 49%;
background-color: #fff;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
</style>
<style scoped>
.tab-title {
background: #F7F7F7;
white-space: nowrap;
padding: 0 30rpx;
box-sizing: border-box;
}
.tab-title-item {
display: inline-block;
height: 84rpx;
line-height: 84rpx;
font-weight: 400;
font-size: 35rpx;
color: #666666;
margin-right: 28rpx;
}
.tab-title-item.active {
border-bottom: 4rpx solid #EB1313;
color: #EB1313;
}
.title {
padding: 32rpx 18rpx;
font-weight: bold;
font-size: 33rpx;
color: #363636;
position: relative;
margin: 0 22rpx;
}
.title::before {
content: "";
width: 7rpx;
height: 51rpx;
background: #EB1313;
border-radius: 3rpx;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
}
.item-content {
margin: 0 22rpx;
background: #FF8C50;
border-radius: 10rpx;
padding: 16rpx 16rpx 0 16rpx;
}
.goods {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.goods-item {
width: 49%;
background-color: #fff;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
</style>