preface
Last year this note started, the result touch fish touch too happy forget this stubble… Back to the point. Initially thought of using onPageScroll this API to achieve, the result is not ideal, a single top effect is ok, but the list top will have obvious lag, poor user experience. Caught the dog head, open SAO operation, to the community, sogou (why is not Baidu and ditch, after all, small program is Tencent home, of course, with sogou) a wild search, as expected, standing on the shoulders of big guys to see further, the following code is uniAPP version.
List (pseudo) suction top implementation
The principle is to monitor the intersection of the target node and the reference node to show and hide the top module, so it is called (pseudo) top.
This paper realizes the effect of list sucking and list folding.
Core API
wx.createSelectorQuery()
Create a SelectorQuery selector to query node information
SelectorQuery.select(string selector)
Gets the first matching selector on the current page
SelectorQuery.selectAll(string selector)
Gets all the selector nodes of the current page
wx.createIntersectionObserver(Object component, Object options)
Create and return an IntersectionObserver object instance that corresponds to the IntersectionObserver API on the Web side
IntersectionObserver.relativeTo(string selector, Object margins)
Specify a selector as the reference node
IntersectionObserver.observe(string targetSelector, function callback)
Used to monitor the change of the intersection status of the specified target node
Get target node
Create a selector using createSelectorQuery(). Use selectAll to get all the target elements of the page. Note that custom components are created using this.createsElectorQuery ().
targetElmEvent(targetElm) {
const me = this
return new Promise((resolve, reject) = > {
// Create a selector to get the listener array
me.createSelectorQuery().selectAll(targetElm).boundingClientRect(function(res) {
// The element cannot be found
// The id must be the same as the id on the element node. I'm going to do something with id, and it looks like there's a problem with numeric ID
if(res.length < 1) {
const tip = JSON.parse(JSON.stringify(me.list))
tip.map(v= > v.id=`tip${v.id}`)
me.targetList = tip
}else {
me.targetList = res
}
resolve(res)
}).exec()
})
}
Copy the code
Setting up listeners
Through wx. CreateIntersectionObserver () to create a listener, incoming current custom component instance this, set attribute observeAll for monitoring multiple nodes at the same time, pay attention to the node consumes too much performance. Set a reference with a relativeTo. Use Observe to monitor the intersections between the target node and the reference.
observerEvent(targetElm, relativeElm) {
const me = this;
this.targetElmEvent(targetElm).then(res= > {
// Create observer set observeAll to monitor multiple nodes simultaneously
const observer = wx.createIntersectionObserver(this, {observeAll: true})
// Set the reference and listen for the specified target
observer.relativeTo(relativeElm).observe(targetElm, res => {
// intersectionRatio is the intersectionRatio. BoundingClientRect is the target boundary
const {id, intersectionRatio, boundingClientRect} = res
const {top, bottom, height} = boundingClientRect
// intersectionRatio is used to judge whether intersecting boundary is exceeded
if (intersectionRatio > 0) {
// Assign the top data
me.fixedData = me.list.filter(v= >id.includes(v.id))[0]
me.fixedShow = true
}else {
// Perform special processing on the upper boundary of the top and tail items of the target to clear the top data
if((id === me.targetList[0].id && top >= 0) || (id === me.targetList[me.targetList.length- 1].id && top <= 0)) {
me.fixedShow = false
me.fixedData = {}
}
}
})
})
}
Copy the code
Additional small features – folding
Control folding by setting the show property for each piece of data.
stackToggleEvent(item) {
const me = this
let count = 0
me.list.map((v, i) = > {
if(v.id===item.id) { v.show=! v.show }if(! v.show) { count+=1
}
if(count === me.list.length) {
me.fixedData = {}
me.fixedShow = false}})}Copy the code
Here is the full code
<template>
<div>
<! -- Reference node -->
<div class="relative"></div>
<! -- Dummy top module -->
<div class="stack-top st-fixed" @click="stackToggleEvent(fixedData)" v-if="fixedShow">{{fixedData.title}}</div>
<! -- Data list -->
<ul v-if="list.length > 0">
<! -- Listen on target -->
<li class="targetTag" :id="`tip${item.id}`" v-for="item of list" :key="item.id">
<div class="stack-top" @click="stackToggleEvent(item)">{{item.title}}</div>
<div class="stack-ct" v-if="item.show">
<ul :class="'foldTag'+item.id">
<li class="sc-k" v-for="child in item.children" :key="child.id">
<div>{{child.name}}</div>
</li>
</ul>
</div>
</li>
</ul>
<! -- Test out of scope -->
<div class="overflow"></div>
</div>
</template>
<script>
export default {
data() {
return {
fixedShow: false.// Whether to display the top suction module
targetList: [], // Listen on the target
fixedData: {}, // Top module data
list: [] / / the data source
}
},
onLoad() {
// Test data
The show attribute is used for collapsibility
this.list = [
{
id: 1.title: 'Part ONE'.show: false.children: [{id: 11.name: 1-1 ' '
},
{
id: 12.name: '2'
},
{
id: 13.name: '1-3'
},
{
id: 14.name: 1-4 ' '
},
{
id: 15.name: '1-5']}, {},id: 2.title: 'Part TWO'.show: false.children: [{id: 21.name: '2-1'
},
{
id: 22.name: '2-2'
},
{
id: 23.name: '2-3'
},
{
id: 24.name: '2-4'
},
{
id: 25.name: '2-5'
},
{
id: 26.name: '2-6']}, {},id: 3.title: 'Part THREE'.show: false.children: [{id: 31.name: '3-1'
},
{
id: 32.name: '3-2'
},
{
id: 33.name: '3-3'
},
{
id: 34.name: '3-4'
},
{
id: 35.name: '3 to 5'
},
{
id: 36.name: '3-6'
},
{
id: 37.name: '3 to 7'
},
{
id: 38.name: '3-8'
},
{
id: 39.name: '3-9'
},
{
id: 391.name: '3-9-1'
},
{
id: 392.name: '3-9-2'
},
]
}
]
},
mounted(){
const me = this
// Start the top sucking function, passing in the target node and the reference node
me.observerEvent('.targetTag'.'.relative')},methods: {
// Implement the folding function and handle the top change when expanding and hiding
stackToggleEvent(item) {
const me = this
let count = 0
me.list.map((v, i) = > {
if(v.id===item.id) { v.show=! v.show }if(! v.show) { count+=1
}
if(count === me.list.length) {
me.fixedData = {}
me.fixedShow = false}})},// Get the listener target and return a promise
targetElmEvent(targetElm) {
const me = this
return new Promise((resolve, reject) = > {
// Create a selector to get the listener array
me.createSelectorQuery().selectAll(targetElm).boundingClientRect(function(res) {
// The element cannot be found
// The id must be the same as the id on the element node. I'm going to do something with id, and it looks like there's a problem with numeric ID
if(res.length < 1) {
const tip = JSON.parse(JSON.stringify(me.list))
tip.map(v= > v.id=`tip${v.id}`)
me.targetList = tip
}else {
me.targetList = res
}
resolve(res)
}).exec()
})
},
// Listen on the target to determine the boundary processing core logic
observerEvent(targetElm, relativeElm) {
const me = this;
this.targetElmEvent(targetElm).then(res= > {
Set observeAll to monitor multiple nodes at the same time. Note that too many nodes will consume performance
const observer = wx.createIntersectionObserver(this, {observeAll: true})
// Set the reference and listen for the specified target
observer.relativeTo(relativeElm).observe(targetElm, res => {
// intersectionRatio is the intersectionRatio. BoundingClientRect is the target boundary
const {id, intersectionRatio, boundingClientRect} = res
const {top, bottom, height} = boundingClientRect
// intersectionRatio is used to judge whether intersecting boundary is exceeded
if (intersectionRatio > 0) {
// Assign the top data
me.fixedData = me.list.filter(v= >id.includes(v.id))[0]
me.fixedShow = true
}else {
// Do a special processing on the top boundary of the target to clear the top data
if((id === me.targetList[0].id && top >= 0) || (id === me.targetList[me.targetList.length- 1].id && top <= 0)) {
me.fixedShow = false
me.fixedData = {}
}
}
})
})
}
}
}
</script>
<style lang="scss" scoped>.relative { position: fixed; top: -2rpx; left: 0; height: 2rpx; width: 100vw; opacity: 0; } .st-fixed { position: fixed; top: 0; left: 0; width: 100%; z-index: 10; box-sizing: border-box; background-color: #ff7a52 ! important; } .stack-top { @include _flex(space-between); padding: 20rpx 30rpx; background-color: #6f9dff; .deg180c { transform: rotate(180deg); } } .stack-ct { padding: 0 30rpx; text-align: right; .sc-k { position: relative; display: inline-block; width: 100%; padding: 30rpx; margin-bottom: 60rpx; box-sizing: border-box; Box-shadow :0 12rpx 36rpx 0 rgba(31,66,209,0.1); border-radius:8rpx; 2 the RPX border: solid rgba (235238255, 1); &:first-child { margin-top: 10rpx; } &:last-child { .line { opacity: 0; } } } .sk-top { @include _flex(flex-start, flex-start, flex-start); .st-time { font-size:24rpx; Color: rgba (2,0,18,1); } } } .overflow { height: 200vh; width: 100vw; background-color: red; }</style>
Copy the code
conclusion
If there is something wrong, please gently advise.