文件
OmniCard/mpuniapp/pages/template/scroll-view-sticky-section/scroll-view-sticky-section.uvue

131 行
3.6 KiB
Plaintext

<template>
<scroll-view ref="scroll" @scroll="onScroll" class="page" bounces="false">
<view class="content-item">
<text class="text">向上滑动页面,体验元素吸顶效果。</text>
</view>
<view v-for="(item,index) in list" :key="index" class="content-item">
<text class="text">first content-{{item}}</text>
</view>
<view ref="sticky" class="search">
<view style="flex-direction: row;">
<image src="/static/template/scroll-fold-nav/search.png" style="width: 15px;" mode="widthFix"></image>
<text class="search-tip-text">请输入你要搜索的内容</text>
</view>
<text class="search-btn">搜索</text>
</view>
<view v-for="(item,index) in list" :key="index" class="content-item">
<text class="text">second content-{{item}}</text>
</view>
<view v-for="(item,index) in list" :key="index" class="content-item">
<text class="text">second content-{{item}}</text>
</view>
</scroll-view>
</template>
<script setup lang="uts">
/**
* @description: 支持在 scroll-view 中让搜索栏吸附到顶部的分段列表页面。
* @logic: 页面在初次渲染时计算吸顶元素相对滚动容器的位置,滚动过程中根据 scrollTop 动态更新搜索栏的 translateY,实现吸顶效果。
* @optimization: 吸顶逻辑只维护一个当前偏移值并直接更新目标元素 transform,避免滚动过程中重复创建或销毁节点。
*/
const list = ref(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15'])
let stickyTop = 0
let stickyTran = 0
const scrollTop = ref(0)
const sticky = ref<UniElement | null>(null)
const scroll = ref<UniElement | null>(null)
function onScroll(e : ScrollEvent) {
if (e.detail.scrollTop > stickyTop) {
let stickyTran_ = e.detail.scrollTop - stickyTop;
if (stickyTran_ != stickyTran) {
sticky.value?.style?.setProperty('transform', 'translateY(' + stickyTran_ + 'px)');
}
stickyTran = stickyTran_;
} else {
sticky.value?.style?.setProperty('transform', '');
stickyTran = 0;
}
}
function back() {
uni.navigateBack({
success(result) {
console.log('navigateBack success', result.errMsg)
},
fail(error) {
console.log('navigateBack fail', error.errMsg)
},
complete(result) {
console.log('navigateBack complete', result.errMsg)
},
})
}
async function calcStickyTop() {
if (sticky.value != null && scroll.value != null) {
const stickyRect = await sticky.value!.getBoundingClientRectAsync()!;
const scrollRect = await scroll.value!.getBoundingClientRectAsync()!;
stickyTop = stickyRect.top - scrollRect.top;
console.log(stickyRect, scrollRect);
}
}
onLoad(() => {
})
onReady(() => {
calcStickyTop()
})
</script>
<style>
.page {
flex: 1;
padding: 0 15px;
background-color: #f5f5f5;
}
.content-item {
padding: 15px;
margin: 5px 0;
background-color: #fff;
border-radius: 5px;
}
.text {
font-size: 14px;
color: #666;
line-height: 20px;
}
.search {
background-color: #FFFFFF;
border: 1px solid #fbdf0d;
height: 35px;
border-radius: 100px;
margin: 0 12px;
padding: 8px;
flex-direction: row;
align-items: center;
justify-content: space-between;
z-index: 100;
}
.search-tip-text {
font-size: 12px;
color: #666;
}
.search-btn {
font-size: 12px;
background-color: #ff6900;
color: #FFF;
padding: 5px 8px;
border-radius: 100px;
}
</style>