账单列表滑动月份统计固定位置

微信,支付宝,银行有个账单列表,按月统计每一条交易记录

滚动列表效果-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>

若有收获,就点个赞吧

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值