首页 > 其他分享 >vue手搓h5滚动日期选择器组件

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

时间:2023-07-18 17:36:37浏览次数:45  
标签:vue 滚动 元素 height year scrollTop mound 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>

 

标签:vue,滚动,元素,height,year,scrollTop,mound,h5,选择器
From: https://www.cnblogs.com/piaohd/p/17563575.html

相关文章

  • vue+zxing 扫描条形码
    背景扫描甲方商品身上的条形码。吐槽下:这玩意又细又小,还反光,最后用的uni-app上的插件,而且不用自己封装了。虽然和我之前的二方案差不多,即使用Quagga,打开video,将每帧画成canvas,然后转换为图片交给Quagga识别,缺点是功耗大,最后实装的表现还行,就是扫几个码,手机热了。回归正题,最开......
  • vue使用hiprint实现打印(vue-plugin-hiprint)
    1、安装插件:npminstallvue-plugin-hiprint或yarnaddvue-plugin-hiprint2、普通使用:<template><divclass="box"><divclass="box-tool"><el-button-group><el-buttontype="primary......
  • vue v-if 和v-permission 共同使用的奇怪问题
    背景后台系统某功能按钮需要订单状态和用户权限共同校验是否显示,将权限校验和v-if共同作用在同一div中,下方为实例代码<divv-if="status==0"><div@click="function1">某按钮功能</div></div><divv-if="status==1"v-permission="['admin......
  • antd+vue3 tree-select 组件库 筛选结果不正确的问题
    第一次遇到这种带搜索框的下拉树状列表搜索关键字的时候出现我不想要的结果。我感觉组件它只是搜索一级列表而没有搜索二级列表,然后一节列表把它整个的二级列表带出来了。二级列表里边包含搜索关键字的所有item才是我想要的。相关代码:1<!--页面名称-->2<div......
  • vue 小写转大写方法
    好久好久没更新了啊,其实有好多可以写的,但是不知道为啥不想写了方法一://输入的金额进行大写转换functiontransformation(val){varfraction=["角","分"];vardigit=["零","壹","贰","叁","肆","伍","陆"......
  • vue前端项目启动
    我们拉取了一个前端项目后,如果项目中有说明的文档,可以参照文档的步骤启动项目,如果项目中没有说明文档,那我们可以按照以下的步骤启动项目1、首先是安装依赖包npminstall 2、启动项目npmrundev这里的npmrun环境名称,这里的环境名称主要取决于项目中的package.json文件中......
  • vue学习——分析脚手架
        ......
  • npm安装教程 搭建vue
    一、相关概念npm:Nodejs下的包管理器。webpack:它主要的用途是通过CommonJS的语法把所有浏览器端需要发布的静态资源做相应的准备,比如资源的合并和打包。vue-cli:用户生成Vue工程模板。(帮你快速开始一个vue的项目,也就是给你一套vue的结构,包含基础的依赖库,只需要npminstall......
  • 介绍社交论坛问答发帖系统源码-java+vue+uniapp开发前后端
    前后端分离社交论坛问答发帖BBS源码,社交论坛小程序|H5论坛|,app论坛是java+vue+uniapp开发的前后端分离社交论坛问答发帖/BBS项目,包括论坛图文帖,视频,圈子,IM私聊,微信支付,付费贴,积分签到,钱包充值等论坛小程序论坛app完整功能演示地址:www.runruncode.com/java/19462.html ......
  • 前端Vue仿微信我的菜单栏组件按钮组件
    随着技术的发展,开发的复杂度也越来越高,传统开发方式将一个系统做成了整块应用,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身。通过组件化开发,可以有效实现单独开发,单独维护,而且他们之间可以随意的进行组合。大大提升开发效率......