【前端】使用jQuery封装一套UI组件 - 移动端时间选择组件

简介: 【首先要声明一点,技术不分好坏,能用好就行,不用鄙视老技术】今天要给大家展示的是移动端时间选择组件最开始,看到比较新颖的移动端时间选择组件是在某款手机上,分年月日下拉选择,UI界面也挺好看的除了讲解实现的步骤以及关键点,文章最后面会有完整的代码
作者:小5聊基础
简介:一只喜欢全栈方向的程序员,欢迎咨询,尽绵薄之力答疑解惑
编程原则:Write Less Do More
  • 选择事件组件效果

image.png

【静态布局】

1)先设置一个父级div,宽度设置100%,高度设置30vh <br/>
vh单位,类似%分号,这里表示的是占30的视窗 <br/>
2)再设置一个父级的遮罩层div,有个技巧,就是背景样式使用rgba(0,0,0,.5) <br/>
r=red=红色,g=green=绿色,b=blue=蓝色,a=alpha=透明度,.5是0.5的简写 <br/>
width:100%;height:100%;position:fixed;top:0px;left:0px; <br/>
3)内容区域的顶部,设置两个按钮,放在左右两边,中间显示选择时间文本 <br/>
按钮可以使用左右浮动方式布局,文本直接根据父级设置的居中样式即可 <br/>

  • 主框架效果

image.png

  • 代码
<!--时间选择组件-->
<div style="width:100%;height:100%;position:fixed;top:0px;left:0px;background:rgba(0,0,0,.3);">
    <div style="width:100%;height:30vh;background:#fff;position:absolute;bottom:0px;left:0px;">
        <div style="width:100%;height:35px;line-height:35px;text-align:center;border-bottom:1px solid #ccc;">
            <span style="float:left;cursor:pointer;padding:0 20px;">取消</span>
            <span>选择时间</span>
            <span style="float:right;cursor:pointer;padding:0 20px;">确定</span>
        </div>
    </div>
</div>

4)接着是关键了,设置伸缩布局,左中右,分别是年月日的选择 <br/>
display:flex;
5)时间区域的父级div,设置宽度100%,高度则使用一个css函数设置 <br/>
calc(30vh - 35px),这个非常有意思,就是整体的高度减去按钮的35px高度,剩下的就是时间区域的高度 <br/>
width:100%;height:calc(30vh - 35px);display:flex;
image.png
6)年份可以设置1970~当前时间,设置多个div,父级设置overflow:auto,自动隐藏,在超出高度后自动显示滚动条,每一个年份div设置宽度100%,高度和行高30px,文本居中 <br/>
7)设置一个渐变遮罩背景 <br/>
渐变样式有浏览器兼容性问题,需要设置多个 <br/>
background: linear-gradient(to bottom, #ffffff, rgba(255, 255, 255, 0), #ffffff);
image.png

8)由于渐变遮罩层默认会覆盖在时间选择上面,导致无法焦点下拉,这个时候就可以给遮罩层设置一个样式, pointer-events: none; <br/>
9)通过上面的效果,4个选项不能在焦点居中,重新调整高度<br/>
整体高度可以设置235px,35px为按钮区域高度,200px为时间选择区域高度,遮罩层自然也是设置200px高度 <br/>

  • 效果

image.png

10)再给中间加上居中线框,凸显焦点<br/>
image.png

【交互设置】

1、生成年份

1)时间范围,1970~当前时间 <br/>
2)给年div设置class=year-div <br/>
3)设置一个选项模板,for循环生成年份选项,将html值追加到年份div里 <br/>

  • 效果

image.png

  • 代码
// 生成年份
function createYear() {
    var timeSelectTemplate = $("#timeSelectTemplate").html().trim();
    var html_year = '';
    var start_year = 1970;
    var min_year = new Date().getFullYear();
    for (var i = min_year; i >= start_year; i--) {
        var dom = $(timeSelectTemplate);
        dom.html(i);
        html_year += dom.prop('outerHTML');
    }
    $(".year-div").html(html_year);
}
createYear();

4)到这里虽然解决年份选项生成,但是通过滚动条下拉,发现不能让第一个时间值下拉到中间焦点,显然不能满足这个需求,这个时候可以通过样式3D转换样式transform: translate3d <br/>
5)通过改变三维坐标的y轴的值来实现年份的选中 <br/>
transform: translate3d(0,0,0); 修改Y轴值得时候,记得一定要加上px单位,否则可能无效 <br/>
6)

2、生成月份

1)逻辑比较简单,直接就是1~12即可 <br/>
image.png

// 生成月份
function createMonth() {
    var timeSelectTemplate = $("#timeSelectTemplate").html().trim();
    var html_month = '';
    var start_month = 1;
    var max_month = 12;
    for (var i = start_month; i <= max_month; i++) {
        var dom = $(timeSelectTemplate);
        dom.html((i < 10 ? ('0' + i) : i));
        html_month += dom.prop('outerHTML');
    }
    $(".month-div").html(html_month);
}
createMonth();

3、生成具体天数

1)具体是多少天,需要年份和月份来决定 <br/>
2)最小值28天,最大值31天,还有29天(闰年)、以及30天 <br/>
3)获取一个月有多少天,这里有个小技巧 <br/>

function getMonthDay(year, month) {
    var days = new Date(year, month + 1, 0).getDate()
    return days
}

4)根据获取到的天数,循环生成即可 <br/>
5)设置默认值,1995-01-01,效果如下 <br/>
image.png

【移动端移动事件】

1)在PC电脑端,可以通过鼠标时间,mousedown、mousemove、mouseup来实现移动 <br/>
2)在phone移动端,可以通过触碰事件,touchstart、touchmove、touchend <br/>
image.png

var div = document.querySelector('div.year-div');
div.addEventListener('touchstart', function (event){
    var event = event || window.event;
    console.log(event.touches[0]);
});

3)参数说明 <br/>

clientX:触摸目标在视口中的x坐标。
clientY:触摸目标在视口中的y坐标。
identifier:标识触摸的唯一ID。
pageX:触摸目标在页面中的x坐标。
pageY:触摸目标在页面中的y坐标。
screenX:触摸目标在屏幕中的x坐标。
screenY:触摸目标在屏幕中的y坐标。
target:触目的DOM节点目标。

4)这里只需要关注Y轴的值,定义开始触碰的Y值变量以及移动点Y值得差值变量 <br/>
5)当离开触点时,根据40的倍数和总移动Y值计算,让年份选中值,显示在居中位置,会有一个滑动的效果
<br/>
为什么时40的倍数,因为每个年份选项高度都是40px <br/>
animate动画方法不支持对transform样式设置效果,可以在回调函数进行设置

var unit_count = Math.round(moveTotalY / 40);
$(".year-div").animate({}, 300, function () {
    $(".year-div").css({ "transform": "translate3d(0," + unit_count * unit + "px,0)" });
});

6)滑动年份和月份时,需要重新计算天数

【主要知识点列表】

编号 语言或插件 知识点 说明
1 jQUery $(dom).animatie() 动画方法,元素样式从一个状态到另一个状态的变化
2 js touchstart 触碰开始点
3 js touchmove 触碰移动点
4 js touchend 触碰结束,离开

【完整代码】

以下代码仅供参考,对组件感兴趣的小伙伴可以精简代码

<!--时间选择组件-->
<div class="text-no-select" style="width:100%;height:100%;position:fixed;top:0px;left:0px;background:rgba(0,0,0,.3);">
    <div style="width:100%;height:235px;background:#fff;position:absolute;bottom:0px;left:0px;">
        <div style="width:100%;height:35px;line-height:35px;text-align:center;border-bottom:1px solid #333;">
            <span style="float:left;cursor:pointer;padding:0 20px;">取消</span>
            <span>选择时间</span>
            <span style="float:right;cursor:pointer;padding:0 20px;">确定</span>
        </div>
        <div style="width:100%;height:200px;display:flex;overflow: hidden;">
            <div class="year-div" style="width:100%;height:auto;transform: translate3d(0,0,0);">

            </div>
            <div class="month-div" style="width:100%;height:auto;transform: translate3d(0,0,0);">

            </div>
            <div class="day-div" style="width:100%;height:auto;transform: translate3d(0,0,0);">

            </div>
        </div>
        <!--居中焦点线框-->
        <div style="width:100%;height:40px;position:absolute;bottom:80px;left:0px;pointer-events: none;border-top:1px solid #ccc;border-bottom:1px solid #ccc;"></div>
        <!--遮罩-->
        <div style="width:100%;height:200px;position:absolute;bottom:0px;left:0px;pointer-events: none;
        background: -webkit-gradient(linear, left top, left bottom, from(#ffffff), color-stop(rgba(255, 255, 255, 0)), to(#ffffff));
        background: -webkit-linear-gradient(top, #ffffff, rgba(255, 255, 255, 0), #ffffff);
        background: linear-gradient(to bottom, #ffffff, rgba(255, 255, 255, 0), #ffffff);"></div>
    </div>
</div>

<!--日期选项模板-->
<script type="text/template" id="timeSelectTemplate">
    <div data-value="" style="width:100%;height:40px;line-height:40px;text-align:center;">
        <span>2022</span>
    </div>
</script>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript">

    $(function () {

        // =====全局参数=====
        var year_value = 1995;
        var month_value = 1;
        var day_value = 1;
        var moveYearTotalY = 0;
        var moveMonthTotalY = 0;
        var moveDayTotalY = 0;
        var unit = 40;

        // 生成年份
        function createYear() {
            var timeSelectTemplate = $("#timeSelectTemplate").html().trim();
            var html_year = '';
            var start_year = 1970;
            var min_year = new Date().getFullYear();
            for (var i = min_year; i >= start_year; i--) {
                var dom = $(timeSelectTemplate);
                dom.html(i);
                dom.attr("data-value", i);
                html_year += dom.prop('outerHTML');
            }
            $(".year-div").html(html_year);

            //设置默认值
            var unit_count = year_value - min_year;
            moveYearTotalY = (unit_count + 2) * unit;
            $(".year-div").css({ "transform": "translate3d(0," + moveYearTotalY + "px,0)" });
        }
        createYear();

        // 生成月份
        function createMonth() {
            var timeSelectTemplate = $("#timeSelectTemplate").html().trim();
            var html_month = '';
            var start_month = 1;
            var max_month = 12;
            for (var i = start_month; i <= max_month; i++) {
                var dom = $(timeSelectTemplate);
                dom.html((i < 10 ? ('0' + i) : i));
                dom.attr("data-value", i);
                html_month += dom.prop('outerHTML');
            }
            $(".month-div").html(html_month);

            //设置默认值
            moveMonthTotalY = 2 * unit;
            $(".month-div").css({ "transform": "translate3d(0," + moveMonthTotalY + "px,0)" });
        }
        createMonth();

        // 生成天数
        function createDay() {
            var timeSelectTemplate = $("#timeSelectTemplate").html().trim();
            var html_day = '';
            var start_day = 1;
            var max_day = getMonthDay(year_value, month_value);
            for (var i = start_day; i <= max_day; i++) {
                var dom = $(timeSelectTemplate);
                dom.html((i < 10 ? ('0' + i) : i));
                dom.attr("data-value", i);
                html_day += dom.prop('outerHTML');
            }
            $(".day-div").html(html_day);

            //设置默认值
            moveDayTotalY = 2 * unit;
            $(".day-div").css({ "transform": "translate3d(0," + moveDayTotalY + "px,0)" });
        }
        createDay();

        function getMonthDay(year, month) {
            var days = new Date(year, month, 0).getDate()
            return days
        }

        // =====滑动事件=====
        // 滑动时间 - 年份
        function moveYear() {

            var name = '.year-div';
            var startY = 0;
            var valueY = 0;
            var div = document.querySelector(name);
            div.addEventListener('touchstart', function (event) {
                var event = event || window.event;
                var touches = event.touches[0];
                startY = touches.clientY;
            });

            div.addEventListener('touchmove', function (event) {
                var event = event || window.event;
                var touches = event.touches[0];
                valueY = touches.clientY - startY + moveYearTotalY;
                $(name).css({ "transform": "translate3d(0," + valueY + "px,0)" });
            });

            div.addEventListener('touchend', function () {
                if (valueY == 0) return;
                moveYearTotalY = valueY;
                var unit_count = Math.round(moveYearTotalY / unit);
                $(name).animate({}, 300, function () {
                    $(name).css({ "transform": "translate3d(0," + unit_count * unit + "px,0)" });
                });
            });
        }
        moveYear();

        // 滑动时间 - 月份
        function moveMonth() {

            var name = '.month-div';
            var startY = 0;
            var valueY = 0;
            var div = document.querySelector(name);
            div.addEventListener('touchstart', function (event) {
                var event = event || window.event;
                var touches = event.touches[0];
                startY = touches.clientY;
            });

            div.addEventListener('touchmove', function (event) {
                var event = event || window.event;
                var touches = event.touches[0];
                valueY = touches.clientY - startY + moveMonthTotalY;
                $(name).css({ "transform": "translate3d(0," + valueY + "px,0)" });
            });

            div.addEventListener('touchend', function (event) {
                if (valueY == 0) return;
                moveMonthTotalY = valueY;
                var unit_count = Math.round(moveMonthTotalY / unit);
                $(name).animate({}, 300, function () {
                    var _y = unit_count * unit;
                    if (_y > 80) _y = 80;
                    $(name).css({ "transform": "translate3d(0," + _y + "px,0)" });
                });
            });
        }
        moveMonth();

        // 滑动时间 - 天数
        function moveDay() {

            var name = '.day-div';
            var startY = 0;
            var valueY = 0;
            var div = document.querySelector(name);
            div.addEventListener('touchstart', function (event) {
                var event = event || window.event;
                var touches = event.touches[0];
                startY = touches.clientY;
            });

            div.addEventListener('touchmove', function (event) {
                var event = event || window.event;
                var touches = event.touches[0];
                valueY = touches.clientY - startY + moveDayTotalY;
                $(name).css({ "transform": "translate3d(0," + valueY + "px,0)" });
            });

            div.addEventListener('touchend', function (event) {
                if (valueY == 0) return;
                moveDayTotalY = valueY;
                var unit_count = Math.round(moveDayTotalY / unit);
                $(name).animate({}, 300, function () {
                    var _y = unit_count * unit;
                    if (_y > 80) _y = 80;
                    $(name).css({ "transform": "translate3d(0," + _y + "px,0)" });
                });
            });
        }
        moveDay();
    });
</script>
相关文章
|
7月前
|
前端开发 算法 Java
【CSS】前端三大件之一,如何学好?从基本用法开始吧!(二):CSS伪类:UI伪类、结构化伪类;通过伪类获得子元素的第n个元素;创建一个伪元素展示在页面中;获得最后一个元素;处理聚焦元素的样式
伪类:伪类这个叫法源自于它们跟类相似,但实际上并没有类会附加到标记中的标签上。 伪类分为两种(以及新增的伪类选择器): UI伪类:会在HTML元素处于某种状态时(例如:鼠标指针位于连接上),为该元素应用CSS样式。 :hover 结构化伪类:会在标记中存在某种结构上的关系时 例如: 某元素是一组元素中的第一个或最后一个,为该元素应用CSS样式。 :not和:target(CSS3新增的两个特殊的伪类选择器)
892 2
|
11月前
|
开发者 容器
44.[HarmonyOS NEXT RelativeContainer案例一] 掌握组件锚点布局:打造灵活精准的UI定位系统
在HarmonyOS NEXT的UI开发中,精确控制组件位置是构建复杂界面的关键。RelativeContainer作为一种强大的布局容器,通过锚点系统提供了精确定位能力,使开发者能够创建出灵活且精准的UI布局。本教程将详细讲解如何使用RelativeContainer的锚点布局功能,帮助你掌握这一核心技术。
338 4
|
11月前
|
设计模式 缓存 容器
06.HarmonyOS Next UI进阶:Text组件与视觉样式完全指南
在HarmonyOS Next应用开发中,Text组件是最基础也是最常用的UI元素之一。它不仅用于显示文本内容,还可以通过丰富的样式属性实现各种视觉效果。掌握Text组件的样式设置,是构建精美UI界面的基础技能。
490 1
|
8月前
|
Linux Go iOS开发
IDA 9.2 发布:Golang 改进、新 UI 组件、类型解析等
IDA Pro 9.2 (macOS, Linux, Windows) - 强大的反汇编程序、反编译器和多功能调试器
1438 0
|
10月前
|
Web App开发 前端开发 JavaScript
Element UI框架中自定义input组件的placeholder样式。
确保这些样式在你的应用程序CSS文件中定义,且该文件已正确加载到项目中。通过以上方法,可以控制Element UI组件中input的placeholder样式,使其满足特定的设计要求。这些更改都是基于CSS伪元素进行的,因此并不会对DOM结构产生改变,保持了原有结构的简洁和高效。
849 12
|
11月前
|
移动开发 开发者
仓颉开发语言入门教程:常见UI组件介绍和一些问题踩坑
仓颉开发语言即将发布一周年,虽已有知名App应用,但教程稀缺且官网文档不够完善。幽蓝君推出系列教程,从零开始系统讲解移动开发。本期介绍常用UI组件:按钮、文本、图片、输入框与搜索框的使用方法及注意事项,帮助开发者快速上手仓颉语言。
|
10月前
|
人工智能 JSON 前端开发
如何解决后端Agent和前端UI之间的交互问题?——解析AG-UI协议的神奇作用
三桥君指出AG-UI协议通过SSE技术实现智能体与前端UI的标准化交互,解决流式传输、实时进度显示、数据同步等开发痛点。其核心功能包括结构化事件流、多Agent任务交接和用户中断处理,具有"一次开发到处兼容"、"UI灵活可扩展"等优势。智能体专家三桥君认为协议将AI应用从聊天工具升级为实用软件,适用于代码生成、多步骤工作流等场景,显著提升开发效率和用户体验。
2389 0
|
安全 API 开发者
深入探索ArkUI中的@LocalBuilder装饰器:构建高效可维护的UI组件
在ArkUI框架中,组件化开发至关重要。@LocalBuilder作为自API version 12引入的装饰器,专注于组件内部私有构建,确保封装性与安全性。本文解析其工作原理、参数传递机制及与@Builder的区别,结合典型场景代码示例,助开发者掌握这一工具。通过状态驱动UI更新、复杂组件组合等实践,优化性能并提升代码可维护性,推动高效UI架构构建。
386 0
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
1526 14
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
928 0

热门文章

最新文章

  • 1
    前端如何存储数据:Cookie、LocalStorage 与 SessionStorage 全面解析
    1088
  • 2
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(九):强势分析Animation动画各类参数;从播放时间、播放方式、播放次数、播放方向、播放状态等多个方面,完全了解CSS3 Animation
    478
  • 3
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(八):学习transition过渡属性;本文学习property模拟、duration过渡时间指定、delay时间延迟 等多个参数
    364
  • 4
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(七):学习ransform属性;本文学习 rotate旋转、scale缩放、skew扭曲、tanslate移动、matrix矩阵 多个参数
    352
  • 5
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(六):全方面分析css的Flex布局,从纵、横两个坐标开始进行居中、两端等元素分布模式;刨析元素间隔、排序模式等
    471
  • 6
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(五):背景属性;float浮动和position定位;详细分析相对、绝对、固定三种定位方式;使用浮动并清除浮动副作用
    637
  • 7
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(四):元素盒子模型;详细分析边框属性、盒子外边距
    1056
  • 8
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(三):元素继承关系、层叠样式规则、字体属性、文本属性;针对字体和文本作样式修改
    250
  • 9
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(二):CSS伪类:UI伪类、结构化伪类;通过伪类获得子元素的第n个元素;创建一个伪元素展示在页面中;获得最后一个元素;处理聚焦元素的样式
    892
  • 10
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(一):CSS发展史;CSS样式表的引入;CSS选择器使用,附带案例介绍
    435