资讯专栏INFORMATION COLUMN

原生js手撸一个日历,支持范围选择(带背景色)

xavier / 2149人阅读

摘要:一个支持范围选择的日历移动端需求原型日历默认显示当前月,并且标注当日的日期,当日以后得日期置灰不可以选择。日历支持选择某单个日期和范围选择,并且选中的两个日期之间要加背景色。

一个支持范围选择的日历(移动端)

需求原型:
1、日历默认显示当前月,并且标注当日的日期,当日以后得日期置灰不可以选择。
2、日历支持选择某单个日期和范围选择,并且选中的两个日期之间要加背景色。
3、限制最多选择6个月,超出六个月提示“最多选择6个月”,并且日历滚动到选择的起始日期位置,保留开始日期的选中效果,继续选择结束日期。
4、在显示周的元素下面,要有个显示当前滚动到的年月的元素并带有动画效果。
demo采用的是rem布局。
需求大概就是这个样子,下面贴代码咯:

具体实现逻辑

index.html

    



    
    日历
    
    
    
    
    
    
    Document
    
    



    
日历范围选择
    最多可选六个月,请重新选择!

    base.css

        html, body {
        width: 100%;
        height: 100%;
      }
      
      #app {
        width: 100%;
        height: 100%;
      }
      
      article,
      aside,
      details,
      figcaption,
      figure,
      footer,
      header,
      hgroup,
      menu,
      nav,
      section {
        display: block;
      }
      
      input,
      select,
      textarea {
        font-size: 100%;
      }
      
      th {
        text-align: inherit;
      }
      
      a img,
      fieldset,
      img {
        border: 0;
      }
      
      ol,
      ul {
        list-style: none;
      }
      
      caption,
      th {
        text-align: left;
      }
      
      h1,
      h2,
      h3,
      h4,
      h5,
      h6 {
        font-size: 100%;
        font-weight: 500;
      }
      
      a:hover {
        text-decoration: underline;
      }
      
      a,
      ins {
        text-decoration: none;
      }
      
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      
      ::-webkit-input-placeholder {
        color: #939799;
      }
      
      :-moz-placeholder {
        color: #939799;
      }
      
      ::-moz-placeholder {
        color: #939799;
      }
      
      input::-ms-clear, input::-ms-reveal {
        display: none;
      }
      
      @media screen and (max-width: 1024px) {
        #app {
          width: auto;
          height: auto;
        }
        .continer {
          position: relative !important;
          width: 100%;
          height: 100% !important;
          display: flex;
          justify-content: center;
          padding-top: 70px !important;
          padding-bottom: 170px;
        }
      }
      
      @media screen and (min-width: 1025px) {
        #app {
          width: 100% !important;
          height: 100% !important;
        }
      }
      
    

    calnder.css

        
    .header-top {
        position: fixed;
        top: 0;
        left: 0;
        background: #fff;
        width: 100%;
        z-index: 999;
      }
      
      .header-top .control-box {
        width: 100%;
        height: 1.6rem;
        font-size: 0.42667rem;
        font-weight: 400;
        color: #363c54;
        padding: 0.53333rem 0.8rem 0.53333rem 0.66667rem;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      
      .header-top .control-box span:nth-child(2) {
        display: inline-block;
        width: 0.53333rem;
        height: 0.53333rem;
        /* background: url("../static/images/close.png") no-repeat center; */
        background-size: cover;
      }
      
      .header-top .pushUp {
        animation: moveDown .5s  alternate;
        -webkit-animation: moveDown .5s  alternate;
        -moz-animation: moveDown .5s  alternate;
        -o-animation: moveDown .5s  alternate;
        -ms-animation: moveDown .5s  alternate;
      }
      
      .header-top .pushDown {
        animation: moveUp .5s  alternate;
        -webkit-animation: moveUp .5s  alternate;
        -moz-animation: moveUp .5s  alternate;
        -o-animation: moveUp .5s  alternate;
        -ms-animation: moveUp .5s  alternate;
      }
      
      @keyframes moveUp {
        0% {
          transform: translateY(-0.8rem);
        }
        50% {
          transform: translateY(0.53333rem);
        }
        100% {
          transform: translateY(0);
        }
      }
      
      @keyframes moveDown {
        0% {
          transform: translateY(-0.8rem);
        }
        50% {
        }
        100% {
          transform: translateY(0);
        }
      }
      
      .header-top #week-item {
        width: 90%;
        height: 1.06667rem;
        list-style: none;
        display: flex;
        text-align: center;
        justify-content: space-around;
        align-items: center;
        margin: 0 auto;
        position: relative;
        background: #fff;
        z-index: 10;
        padding-right: 0.34667rem;
      }
      
      .header-top #week-item::after {
        background-color: #f0f1f2;
        content: "";
        position: absolute;
        left: 0;
        bottom: 0;
        width: 100%;
        height: 1px;
        transform-origin: 0 0;
      }
      
      .header-top #week-item li {
        font-size: 0.37333rem;
        font-weight: 400;
        color: #363c54;
      }
      
      .header-top .current-time {
        width: 100%;
        text-align: center;
        font-size: 0.37333rem;
        font-weight: 400;
        color: #9b9da9;
        background: #fff;
      }
      
      .header-top .current-time #current-time {
        height: 1.04rem;
        line-height: 1.04rem;
        text-align: center;
        font-size: 0.37333rem;
        font-weight: 400;
        color: #9b9da9;
        border: none;
        background: #fff;
        transition: .3s all;
        border-color: transparent;
        -webkit-appearance: none;
      }
      
      .main {
        width: 100%;
        height: 100%;
      }
      
      .main .current-month {
        width: 90%;
        height: 1.06667rem;
        line-height: 1.06667rem;
        font-size: 0.37333rem;
        font-weight: 400;
        text-align: center;
        margin: 0.13333rem auto;
        color: #9b9da9;
        position: relative;
      }
      
      .main .current-month::before {
        background-color: #f0f1f2;
        content: "";
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 1px;
        transform-origin: 0 0;
      }
      
      .main .l-border {
        border-radius: 50% 0 0 50% !important;
      }
      
      .main .r-border {
        border-radius: 0 50% 50% 0 !important;
      }
      .main .calendar__dialog{
          display: none;
          width: 7.73333rem;
          height: 1.73333rem;
          line-height: 1.73333rem;
          background: rgba(0, 0, 0, 0.8);
          border-radius: 0.13333rem;
          position: fixed;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
          text-align: center;
          font-size: 0.42667rem;
          font-weight: 400;
          color: white;
          z-index: 999;
      }
      
      .mode-one {
        width: 100%;
        align-content: space-between;
        margin-top: 2.93333rem;
        background: #fff;
        padding-bottom: 2rem;
      }
      
      .mode-one .month {
        position: relative;
        color: #141414;
        width: 100%;
        padding: 0 0.64rem 0 0.72rem;
      }
      
      .mode-one .month .after-dom {
        width: 100%;
        height: 1rem;
        position: absolute;
        left: 0;
        bottom: 0;
        z-index: 10;
      }
      
      .mode-one .dateItem {
        cursor: pointer;
      }
      
      .mode-one .disabled {
        color: #cdced4 !important;
      }
      
      .mode-one .cover-background {
        background: #20c997;
      }
      
      .mode-one .month .item {
        display: inline-block;
        width: 1.2rem;
        height: 1.2rem;
        font-size: 0.37333rem;
        line-height: 1.2rem;
        font-weight: 400;
        text-align: center;
        vertical-align: middle;
        position: relative;
        color: #363C54;
        margin: 0.12rem 0;
        z-index: 11;
      }
      
      .mode-one .now::after {
        background-color: #20c997;
        content: "";
        position: absolute;
        left: 50%;
        margin-left: -1.5px;
        bottom: 3px;
        width: 3px;
        height: 3px;
        border-radius: 50%;
        transform-origin: 0 0;
      }
      
      .mode-one .month .active {
        background: #20c997;
        border-radius: 50%;
      }
      
      .calendar-content {
        width: 100%;
        height: 100%;
        overflow: scroll;
      }
      
      .calendar__box {
        width: 100%;
        height: 100%;
        z-index: 998;
      }
      
      .footer-box {
        width: 100%;
        height: 2rem;
        background: white;
        position: fixed;
        left: 0;
        bottom: 0;
        display: flex;
        justify-content: center;
        align-items: center;
        -webkit-transform: translateZ(0);
        margin-top: 2rem;
        z-index: 12;
      }
      
      .footer-box div {
        width: 8.66667rem;
        height: 1.2rem;
        line-height: 1.2rem;
        background: #CDCED4;
        border-radius: 0.6rem;
        text-align: center;
        font-size: 0.42667rem;
        font-weight: 400;
        color: white;
      }
      
      .cover_back {
        background: #20c997 !important;
      }
      
      .hideCalendar {
        transition: .3 all;
        transform: translateX(-100%, 0) !important;
      }
      
    

    resize.js

    ; (function () {
        resize()
        window.onresize = resize;
    })()    
    
    function resize () {
        if (document.documentElement.clientWidth >= 750) {
            document.documentElement.style.fontSize = "75px";
            return
        }
        document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + "px"
    }
    

    utils.js

        let Utils = {
        getElement(selector) {
            return document.querySelector(selector);
        },
        getAllElement(selector) {
            return document.querySelectorAll(selector);
        },
        timePad(time) {
            return time < 10 ? "0" + time : time;
        }
    }
    function daysInMonth(month, year) {
        return new Date(year, month + 1, 0).getDate();
    }
    
    function _Set(data_id, data) {
        if (data_id != "") {
            if (data) {
                var lsobj = window.localStorage;
                var datajson = JSON.stringify(data);
                lsobj.setItem(data_id, datajson);
            }
        }
    }
    
    function _Get(data_id) {
        if (data_id != "") {
            var data = null;
            var lsdata = window.localStorage;
            try {
                var datajson = lsdata.getItem(data_id);
                datajson = JSON.parse(datajson);
                data = datajson;
            } catch (e) {
    
            } finally {
                return data;
            }
        }
    }
    
    
    let scrollAction = {
        x: "undefined",
        y: "undefined"
    },
    scrollDirection;
    
    // 判断页面滚动方向
    function scrollFunc() {
    if (typeof scrollAction.x == "undefined") {
        scrollAction.x = window.pageXOffset;
        scrollAction.y = window.pageYOffset;
    }
    let diffX = scrollAction.x - window.pageXOffset;
    let diffY = scrollAction.y - window.pageYOffset;
    if (diffX < 0) {
        // Scroll right
        scrollDirection = "right";
    } else if (diffX > 0) {
        // Scroll left
        scrollDirection = "left";
    } else if (diffY < 0) {
        // Scroll down
        scrollDirection = "down";
    } else if (diffY > 0) {
        // Scroll up
        scrollDirection = "up";
    } else {
        // First scroll event
    }
    scrollAction.x = window.pageXOffset;
    scrollAction.y = window.pageYOffset;
    };
    
    // 检测两个div是否重叠
    function collision(target, current) {
        let targetX = target.offsetLeft;
        let targetY = target.offsetTop;
        let targetW = target.offsetWidth;
        let targetH = target.offsetHeight;
        let currentX = current.offsetLeft;
        let currentY = current.getBoundingClientRect().top;
        let currentW = current.offsetWidth;
        let currentH = current.offsetHeight;
        return (targetX + targetW > currentX && targetX < currentX + currentW && targetY + targetH > currentY &&
            targetY < currentY + currentH);
    }
    
    // 计算当前选择日期往后推6个月
    function calcDate(str) {
        str = str.replace(/-/g, "/");
        let date = new Date(str);
        //日期转文本方式一:
        let year = date.getFullYear(); //年
        let month = date.getMonth() + 7; //月 +6个月  因为js里month从0开始,所以要加1
        if (month > 12) {
            year++;
            month -= 12;
        }
        let date2 = new Date(year, month, 0); //新的年月
        let day1 = date.getDate();
        let day2 = date2.getDate();
        if (day1 > day2) { //防止+6月后没有31天
            day1 = day2;
        }
        str = year + "-" +
            Utils.timePad(month) +
            "-" +
            Utils.timePad(day1);
        return str;
    }
    
    // 获取两个日期之间的值
    Date.prototype.format = function () {
        let s = "";
        let mouth = (this.getMonth() + 1) >= 10 ? (this.getMonth() + 1) : ("0" + (this.getMonth() + 1));
        let day = this.getDate() >= 10 ? this.getDate() : ("0" + this.getDate());
        s += this.getFullYear() + "-"; // 获取年份。
        s += mouth + "-"; // 获取月份。
        s += day; // 获取日。
        return (s); // 返回日期。
    };
    // 获取两个日期之间的值
    function getAll(begin, end) {
        let arr = [];
        let ab = begin.split("-");
        let ae = end.split("-");
        let db = new Date();
        db.setUTCFullYear(ab[0], ab[1] - 1, ab[2]);
        let de = new Date();
        de.setUTCFullYear(ae[0], ae[1] - 1, ae[2]);
        let unixDb = db.getTime() - 24 * 60 * 60 * 1000;
        let unixDe = de.getTime() - 24 * 60 * 60 * 1000;
        for (let k = unixDb; k <= unixDe;) {
            //console.log((new Date(parseInt(k))).format());
            k = k + 24 * 60 * 60 * 1000;
            arr.push((new Date(parseInt(k))).format());
        }
        return arr;
    }
    // 比较两个日期大小
    function CompareDate(d1, d2) {
        return ((new Date(d1.replace(/-/g, "/"))) < (new Date(d2.replace(/-/g, "/"))));
    }
    
    function getPhoneType() {
        //正则,忽略大小写
        var pattern_phone = new RegExp("iphone", "i");
        var pattern_android = new RegExp("Android", "i");
        var userAgent = navigator.userAgent.toLowerCase();
        var isAndroid = pattern_android.test(userAgent);
        var isIphone = pattern_phone.test(userAgent);
        var phoneType = "phoneType";
        if (isAndroid) {
            var zh_cnIndex = userAgent.indexOf("-");
            var spaceIndex = userAgent.indexOf("build", zh_cnIndex + 4);
            var fullResult = userAgent.substring(zh_cnIndex, spaceIndex);
            phoneType = fullResult.split(";")[1];
        } else if (isIphone) {
            //6   w=375    6plus w=414   5s w=320     5 w=320
            var wigth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
            if (wigth > 400) {
                phoneType = "iphone6 plus";
            } else if (wigth > 370) {
                phoneType = "iphone6";
            } else if (wigth > 315) {
                phoneType = "iphone5s";
            } else {
                phoneType = "iphone 4s";
            }
        } else {
            phoneType = "您的设备太先进了";
        }
        return phoneType;
    }
    
    function isIphonex() {
        if (typeof window !== "undefined" && window) {
            return /iphone/gi.test(window.navigator.userAgent) && window.screen.height >= 812;
        }
        return false;
    }
    

    end ···
    代码有待优化,日后再说吧

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/105211.html

    相关文章

    • 手机端简洁日历控件iantoo.week()

      摘要:主要是为了避免用户修改了手机日历之后,获取的本地与服务器时间不统一可以通过获取服务器时间进行手动设置。日历控件更新之后哦调用只有在调用了的时候才会被触发非必填。对于设备已经通过阻止冒泡事件进行控制不需要再做控制。 原文链接 使用 引入文件: ./build/css/iantooweek.css ./build/js/iantooweek.js 并在页面上调用: iantoo.week(...

      Wuv1Up 评论0 收藏0
    • win10日历交互效果(进阶)

      摘要:日历可视移动高亮范围本篇文章在前一个初级的基础上进行后续的体验优化目标效果鼠标在目标元素内进行移动,个块组成的圆形高亮会随之移动实现效果图原图进阶实现图技术点初级篇使用的渐变范围写法进阶篇使用的渐变范围写法第一种写法是不考虑高光范围移动 win10日历可视移动高亮范围 本篇文章在前一个初级的基础上进行后续的体验优化 目标效果 鼠标在目标元素内进行移动,9个块组成的圆形高亮会随之移动 实...

      skinner 评论0 收藏0
    • 一款基于移动端的日历控件iantooDate

      摘要:已被移除过时的提醒时间的颜色。默认当日历控件滑动的时候是否实时更新日历控件的位置主要是对部分低端机型做性能处理。返回当前的时间调用该方法关闭日历控件。年月日时分秒星期 原文链接 使用 引入文件: ./build/css/iantooDate.css ./build/js/iantooDate.js 并在页面上调用: iantoo.date() 详细使用方法见page/iantooDat...

      liangzai_cool 评论0 收藏0
    • Android通知栏介绍与适配总结(上篇)

      摘要:修改记录版本的通知栏消息功能上并未发生变化,右上角的缩减为了。增加了,允许可穿戴设备远程控制通知栏消息。锁屏状态下,可以控制通知栏消息的隐私程度。但是谷歌规定,自定义布局展示的通知栏消息最大高度是。具体适配不正常的机型有。 此文已由作者黎星授权网易云社区发布。 欢迎访问网易云社区,了解更多网易技术产品运营经验。 由于历史原因,Android在发布之初对通知栏Notification的设...

      fai1017 评论0 收藏0

    发表评论

    0条评论

    xavier

    |高级讲师

    TA的文章

    阅读更多
    最新活动
    阅读需要支付1元查看
    <