微信,支付宝,银行有个账单列表,按月统计每一条交易记录
滚动列表效果-CSDN直播
微信的效果是滑动的时候统计月份数按原有样式固定
样式直接就可以解决
position: sticky;top:0; 就可以解决
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
<title></title>
</head>
<style>
body {
background: #f7f8fa;
margin: 0;
padding: 0;
}
#app {
display: flex;
flex-direction: column;
height: 100vh;
}
.bill-scroll {
flex: 1;
overflow: auto;
}
.item-list-con {
height: 150px;
line-height: 150px;
text-align: center;
}
.item-list-time {
color: #c8c9cc;
padding-bottom: 10px;
}
.item-list {
border-radius: 4px;
background: #fff;
margin-bottom: 20px;
}
#loadmore {
text-align: center;
}
.item-date,
.item-date-scroll {
padding: 20px 10px;
}
.item-date-scroll {
position: absolute;
background: #FFA852;
width: 100%;
}
.loadmore-area {
margin: 20px 0;
}
.bill-count {
line-height: 60px;
margin: 0 10px;
}
.item-date {
position: sticky;
top: 0;
}
</style>
<body>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">
<div class="bill-count">最近{{allmonth}}月的消费记录</div>
<div class="bill-scroll">
<template v-for="(item,index) in list" :key="index">
<div class="item-date" :style="{background:item.color}"
v-show="!list[index-1]||list[index-1].time!==item.time">
<div class="item-date-text">{{item.time}}</div>
</div>
<div class="item-list" :data-index="index" :data-time="item.time">
<div class="item-list-con"> {{item.text}}</div>
<div class="item-list-time">{{item.time}}</div>
</div>
</template>
<div class="loadmore-area" v-show="allmonth">
<div id="loadmore">loadmore</div>
</div>
</div>
</div>
<script>
const { createApp, ref } = Vue
createApp({
setup() {
return {
list: ref([]),
allmonth: 12,
restmonth: 20,
loading: false,
}
},
mounted() {
observer = new IntersectionObserver(
(entries) => {
let loading = entries.find((item) => {
return item.target.id === 'loadmore';
})
// 加载更多处理
if (loading?.isIntersecting) {
// console.log(loading)
this.loadMore()
}
}, {
threshold: [0, 1]
});
// 加载更多
observer?.observe(document.getElementById('loadmore'));
this.list = this.list.concat(this.getData())
},
methods: {
loadMore() {
if (!this.restmonth || this.loading) return
this.loading = true
setTimeout(() => {
this.restmonth--
this.list = this.list.concat(this.getData())
this.loading = false
}, 1000);
},
getData() {
let data = [];
let colorList = ['#FFE0FA', '#FFE0EB', '#FFE5E0', '#FFF5E0', '#F5E0FF',
'#FAFFE0', '#E5E0FF', '#EBFFE0', '#E0EBFF', '#E0FAFF', '#E0FFF5', '#E0FFE5']
for (let i = 0; i < 10; i++) {
data.push({
time: this.formateDate(),
text: '测试数据',
color: colorList[Math.floor(Math.random() * colorList.length)],
})
}
return data
},
formateDate() {
let reduceMonth = this.allmonth - this.restmonth
let month = new Date().getMonth() - reduceMonth
let year = new Date().getFullYear()
if (month < 0) {
year = year + Math.ceil(month / 12) - 1
}
if (month < 0) {
month = 12 + month % 12
}
let curTime = new Date(year, month, 1)
return curTime.getFullYear() + '-' + (curTime.getMonth() + 1) + '-' + curTime.getDate()
}
},
unMounted() {
observer?.disconnect();
}
}).mount('#app')
</script>
</body>
</html>
但是 产品肯定要的不一样的像支付宝和银行一样
原有位置显眼一点,固定的就朴素一点
那就用IntersectionObserver处理
IntersectionObserver 是一个浏览器原生API,用于监测DOM元素是否与其祖先元素(通常是视口)发生交叉,或者与视口的边界相交。这个API可以用于实现许多功能
应用:懒加载,无限滚动,动画滚动,性能优化
demo的触发加载更多使用的也是IntersectionObserver
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
<title></title>
</head>
<style>
body {
background: #f7f8fa;
margin: 0;
padding: 0;
}
#app {
display: flex;
flex-direction: column;
height: 100vh;
}
.bill-scroll {
flex: 1;
overflow: auto;
}
.item-list-con {
height: 150px;
line-height: 150px;
text-align: center;
}
.item-list-time {
color: #c8c9cc;
padding-bottom: 10px;
}
.item-list-area {
padding: 0 10px;
}
.item-list {
border-radius: 4px;
background: #fff;
margin-bottom: 20px;
}
#loadmore {
text-align: center;
}
.item-date,
.item-date-scroll {
padding: 20px 10px;
}
.item-date-scroll {
position: absolute;
background: #FFA852;
left: 0;
right: 0;
}
.loadmore-area {
margin: 20px 0;
}
.bill-count {
line-height: 60px;
margin: 0 10px;
}
</style>
<body>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">
<div class="bill-count">最近{{allmonth}}月的消费记录</div>
<div class="bill-scroll" @scroll="scroll">
<div class="item-date-scroll" v-show="showScrollTime&&scrollTime">{{scrollTime}}</div>
<div v-for="(item,index) in list" :key="index" class="item-list-area">
<div class="item-date" :style="{background:item.color}"
v-show="!list[index-1]||list[index-1].time!==item.time">
<div class="item-date-text">{{item.time}}</div>
</div>
<div class="item-list" :data-index="index" :data-time="item.time">
<div class="item-list-con"> {{item.text}}</div>
<div class="item-list-time">{{item.time}}</div>
</div>
</div>
<div class="loadmore-area" v-show="allmonth">
<div id="loadmore">loadmore</div>
</div>
</div>
</div>
<script>
const { createApp, ref } = Vue
let observer = null
let objCount = {}
createApp({
setup() {
return {
list: ref([]),
allmonth: 12,
restmonth: 20,
loading: false,
scrollTime: ref(''),
showScrollTime: ref(false)
}
},
mounted() {
observer = new IntersectionObserver(
(entries) => {
let loading = entries.find((item) => {
return item.target.id === 'loadmore';
})
// 加载更多处理
if (loading?.isIntersecting) {
// console.log(loading)
this.loadMore()
}
let tpm = entries.map((item) => {
let index = item.target.dataset.index;
if (item.isIntersecting) {
objCount[index] = {
isIntersecting: item.isIntersecting,
time: item.target.dataset.time
};
} else if (objCount[index]) {
delete objCount[index];
}
return {
isIntersecting: item.isIntersecting,
top: item.boundingClientRect.top,
index: item.target.dataset.index,
};
});
// console.log(JSON.stringify(tpm));
for (let key in objCount) {
if (objCount[key]?.isIntersecting) {
this.scrollTime = objCount[key].time;
break;
}
}
}, {
threshold: [0, 1]
});
// 加载更多
observer?.observe(document.getElementById('loadmore'));
this.list = this.list.concat(this.getData())
//加载完成 监听dom
this.$nextTick(() => {
this.listenItem()
})
},
methods: {
scroll(e) {
if (e.target.scrollTop >= 60) {
this.showScrollTime = true
} else {
this.showScrollTime = false
}
},
loadMore() {
if (!this.restmonth || this.loading) return
this.loading = true
setTimeout(() => {
this.restmonth--
this.list = this.list.concat(this.getData())
//加载完成 监听dom
this.$nextTick(() => {
this.listenItem()
})
this.loading = false
}, 1000);
},
getData() {
let data = [];
let colorList = ['#FFE0FA', '#FFE0EB', '#FFE5E0', '#FFF5E0', '#F5E0FF',
'#FAFFE0', '#E5E0FF', '#EBFFE0', '#E0EBFF', '#E0FAFF', '#E0FFF5', '#E0FFE5']
for (let i = 0; i < 10; i++) {
data.push({
time: this.formateDate(),
text: '测试数据',
color: colorList[Math.floor(Math.random() * colorList.length)],
})
}
return data
},
listenItem() {
document.querySelectorAll('.item-list').forEach((item) => {
observer?.observe(item);
});
},
formateDate() {
let reduceMonth = this.allmonth - this.restmonth
let month = new Date().getMonth() - reduceMonth
let year = new Date().getFullYear()
if (month < 0) {
year = year + Math.ceil(month / 12) - 1
}
if (month < 0) {
month = 12 + month % 12
}
let curTime = new Date(year, month, 1)
return curTime.getFullYear() + '-' + (curTime.getMonth() + 1) + '-' + curTime.getDate()
}
},
unMounted() {
observer?.disconnect();
}
}).mount('#app')
</script>
</body>
</html>
若有收获,就点个赞吧