131 行
3.6 KiB
Plaintext
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>
|