vue手搓h5滚动日期选择器组件

背景

新项目为了省事和后台写一起了,所以用不了Uni-app(悲),然后element-ui的日期选择器h5不适配,看着也难受,就想找个好用的,结果找了一圈感慨,自己写个吧。

 

说明

 为了加快速度,代码可能有些臃肿,但大概就是这样了,看着代码好多,其实只要会一个的滚动就会多个了。建议下看下参考文章,然后研究下单个滚动选择器是怎么实现的。

参考文章

(66条消息) vue滚动选择器效果的实现(文字动态效果)_vue实现滚动效果_是浅笑耶的博客-CSDN博客

 

代码

<template>
    <div class="picker">
        <div class="box-picker">
            <div class="solidUl" @scroll="scrollChange">
                <ul>
                    <li id="year" v-for="(item, index) in yearList" :key="index">{{ item }}年</li>
                </ul>
            </div>
        </div>
        <div class="box-picker">
            <div class="solidUl" @scroll="scrollChange1">
                <ul>
                    <li id="mound" v-for="(item, index) in moundList" :key="index">{{ item }}月</li>
                </ul>
            </div>
        </div>
        <div class="box-picker">
            <div class="solidUl" @scroll="scrollChange2">
                <ul>
                    <li id="day" v-for="(item, index) in dayList" :key="index">{{ item }}日</li>
                </ul>
            </div>
        </div>

        <div class="topButton">
            <div> </div>
            <div>请选择</div>
            <div @click="handleClose">完成</div>
        </div>
        <!-- 滚动过程中滚动到的所需位置 -->
        <div class="backgroundFloat"></div>
    </div>
</template>

<script>
export default {
    name: 'datePicker',
    data() {
        return {
            yearList: [],
            moundList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
            dayList: [],
            bigMound: [1, 3, 5, 7, 8, 10, 12],
            smallMound: [4, 6, 9, 11],
            day: 14,
            year: 2023,
            mound: 7,
            height: 160,
            distance: 30,
        }
    },
    watch: {
        year() {
            if (this.mound == 2) {
                this.dayList = []

                if ((this.year % 4 == 0 && this.year % 100 != 0) || (this.year % 400 == 0)) {
                    for (let j = 1; j <= 29; j++) {
                        this.dayList.push(j)
                    }
                } else {
                    for (let j = 1; j <= 28; j++) {
                        this.dayList.push(j)
                    }
                }
            }
        },
        mound() {
            this.dayList = []
            if (this.mound == 2) {
                if ((this.year % 4 == 0 && this.year % 100 != 0) || (this.year % 400 == 0)) {
                    for (let j = 1; j <= 29; j++) {
                        this.dayList.push(j)
                    }
                } else {
                    for (let j = 1; j <= 28; j++) {
                        this.dayList.push(j)
                    }
                }

            } else {
                let arr = this.bigMound.filter(e => {
                    return e == this.mound
                })
                if (arr.length > 0) {
                    for (let j = 1; j <= 31; j++) {
                        this.dayList.push(j)
                    }
                }
                else {
                    for (let j = 1; j <= 30; j++) {
                        this.dayList.push(j)
                    }
                }
            }
        }
    },
    mounted() {
        for (let i = 2024; i <= 2100; i++) {
            this.yearList.push(i)
        }
        for (let j = 1; j <= 31; j++) {
            this.dayList.push(j)
        }
        var scrollList = document.querySelectorAll('.solidUl')
        // 触发监听
       
        this.$nextTick(() => {
            scrollList.forEach(e => {
                e.scrollTop = e.scrollTop + 1
            })
        })

    },
    methods: {
        scrollChange(e) {
            //首先获取到li元素
            var year = document.querySelectorAll('#year')

            //对li元素进行遍历
            for (var i = 0; i < year.length; i++) {
                //offsetTop返回当前元素相对于节点顶部偏移量,scrollTop返回一个元素垂直滚动的距离
                //在滚动过程中,lis[i].offsetTop-e.target.scrollTop可获取到当前元素距离顶部的位置
                //打印出结果自己对比,选择自己需要范围的值,我给li设置的高度为40px,因此185与225之间相差${distance},
                //而${height}与150+${distance}是我实际输出对比测量的我需要元素滚动到该位置时设置样式。
                if ((year[i].offsetTop - e.target.scrollTop) > this.height && (year[i].offsetTop - e.target.scrollTop) < this.height + this.distance) {
                    //当元素滚动到当前位置时,先将元素原来为"active2"的class属性去掉
                    //再给元素增加"active"的属性
                    // console.log(year[i].textContent);
                    this.year = year[i].textContent.slice(0, 4)
                    year[i].classList.remove("active2")
                    year[i].classList.add("active")
                    //给当前元素以上的元素动态设置文字大小,离当前文字越远,文字大小越小
                    for (var j = 1; j <= i; j++) {
                        year[i - j].style.fontSize = (20 - 2 * j) + 'px'
                    }
                    //给当前元素以下的元素动态设置文字大小,离当前文字越远,文字大小越小
                    for (var a = 1; a < year.length - i; a++) {
                        year[i + a].style.fontSize = (20 - 2 * a) + 'px'
                    }
                    //当没滚动到期待位置时,也就是其他所有元素,移除"active"的样式,增加"active2"的样式
                } else {
                    year[i].classList.remove("active")
                    year[i].classList.add("active2")
                }
            }
        },
        scrollChange1(e) {
            //首先获取到li元素
            var mound = document.querySelectorAll('#mound')
            //对li元素进行遍历
            for (var i = 0; i < mound.length; i++) {
                //offsetTop返回当前元素相对于节点顶部偏移量,scrollTop返回一个元素垂直滚动的距离
                //在滚动过程中,lis[i].offsetTop-e.target.scrollTop可获取到当前元素距离顶部的位置
                //打印出结果自己对比,选择自己需要范围的值,我给li设置的高度为40px,因此185与225之间相差${distance},
                //而${height}与150+${distance}是我实际输出对比测量的我需要元素滚动到该位置时设置样式。
                if ((mound[i].offsetTop - e.target.scrollTop) > this.height && (mound[i].offsetTop - e.target.scrollTop) < this.height + this.distance) {
                    //当元素滚动到当前位置时,先将元素原来为"active2"的class属性去掉
                    //再给元素增加"active"的属性
                    this.mound = mound[i].textContent.slice(0, -1)

                    mound[i].classList.remove("active2")
                    mound[i].classList.add("active")
                    //给当前元素以上的元素动态设置文字大小,离当前文字越远,文字大小越小
                    for (var j = 1; j <= i; j++) {
                        mound[i - j].style.fontSize = (20 - 2 * j) + 'px'
                    }
                    //给当前元素以下的元素动态设置文字大小,离当前文字越远,文字大小越小
                    for (var a = 1; a < mound.length - i; a++) {
                        mound[i + a].style.fontSize = (20 - 2 * a) + 'px'
                    }
                    //当没滚动到期待位置时,也就是其他所有元素,移除"active"的样式,增加"active2"的样式
                } else {
                    mound[i].classList.remove("active")
                    mound[i].classList.add("active2")
                }
            }
        },
        scrollChange2(e) {
            //首先获取到li元素
            var day = document.querySelectorAll('#day')
            //对li元素进行遍历
            for (var i = 0; i < day.length; i++) {
                //offsetTop返回当前元素相对于节点顶部偏移量,scrollTop返回一个元素垂直滚动的距离
                //在滚动过程中,lis[i].offsetTop-e.target.scrollTop可获取到当前元素距离顶部的位置
                //打印出结果自己对比,选择自己需要范围的值,我给li设置的高度为40px,因此185与225之间相差${distance},
                //而${height}与150+${distance}是我实际输出对比测量的我需要元素滚动到该位置时设置样式。
                if ((day[i].offsetTop - e.target.scrollTop) > this.height && (day[i].offsetTop - e.target.scrollTop) < this.height + this.distance) {
                    //当元素滚动到当前位置时,先将元素原来为"active2"的class属性去掉
                    //再给元素增加"active"的属性
                    this.day = day[i].textContent.slice(0, -1)
                    day[i].classList.remove("active2")
                    day[i].classList.add("active")
                    //给当前元素以上的元素动态设置文字大小,离当前文字越远,文字大小越小
                    for (var j = 1; j <= i; j++) {
                        day[i - j].style.fontSize = (20 - 2 * j) + 'px'
                    }
                    //给当前元素以下的元素动态设置文字大小,离当前文字越远,文字大小越小
                    for (var a = 1; a < mound.length - i; a++) {
                        day[i + a].style.fontSize = (20 - 2 * a) + 'px'
                    }
                    //当没滚动到期待位置时,也就是其他所有元素,移除"active"的样式,增加"active2"的样式
                } else {
                    day[i].classList.remove("active")
                    day[i].classList.add("active2")
                }
            }
        },
        handleClose(){
            console.log(121);
            this.$emit('handleClose')
        }
    },
}
</script>

<style lang="scss" scoped>
.picker {
    display: flex;
    // border: 1px solid hsl(0, 0%, 60%);
    align-items: center;
    box-sizing: border-box;
    // height: 200px;
    overflow: hidden;
    position: relative;
}

.topButton {
    width: 100%;
    padding-bottom: 10px;
    padding-top: 3px;
    position: absolute;
    top: 0;
    display: flex;
    background: rgb(247, 247, 248);

}

.topButton>div {
    flex: 1;
}

.topButton>div:nth-child(2n) {
    text-align: center;
}

.topButton>div:nth-child(2n-1) {
    text-align: right;
    padding-right: 10px;
    color: #45db2b;
}

.solidUl::-webkit-scrollbar {
    display: none
}

.box-picker {
    height: 300px;
    width: 300px;
}

.solidUl {
    height: 100%;
    //滚动设置在div内
    overflow-y: scroll;
    //增加滚动的流畅性
    touch-action: pan-y;
    -webkit-overflow-scrolling: touch;
}

ul {
    padding: 170px 0 100px 0;
    margin: 0;
    background-color: #fff;

    li {
        list-style: none;
        font-size: 18px;
        line-height: 30px;
        text-align: center;
        opacity: 0.3;
        height: 30px;
        background-color: #fff;
    }
}

.active {
    font-weight: 400;
    font-size: 20px !important;
    color: #333;
    //不透明度需要高点,因为有backgroundFloat颜色的影响
    opacity: 1.2 !important;
    top: 150px;
}

.active2 {
    color: #333;
    //设置不透明度
    opacity: 0.6 !important;
}

//给滚动到的所需位置增加背景色等样式
.backgroundFloat {
    width: 100%;
    height: 30px;
    background-color: #d1d1d1;
    //降低不透明度,以防遮挡文字
    opacity: 0.2;
    position: absolute;
    top: 170px;
    border: 1px solid #999;
}
</style>

 

posted @ 2023-07-18 17:19  piaohd  阅读(1617)  评论(0)    收藏  举报