基于面向过程的城市联动器

作者: 前端一小卒 | 来源:发表于2017-04-14 11:16 被阅读99次

    在做项目中,一个城市联动器效果吸引了我的注意,虽然在项目中是引入第三方插件实现功能,但是做完项目之后自己还是想自己动手将效果实现。限于自己的水平,目前还是使用面向过程的形式将效果给做出来,后期会对代码进行修改。

    页面结构和基础样式

    index.html

    <link rel="stylesheet" href="css/style.css">
    <script src="js/cityaddr.js"></script>
    <input type="text" id="input" readonly="readonly" />
    <div class="_layer_big" style="display: none">
        <div class="_layer">
        </div>
        <div class="layer_box">
            <div class="cityHtml" style="position: relative;" data-pro="" data-city="" data-urban="">
                <div class="pro_html">
                    <ul id="proUl" class="transform_ul" style="transform: translateY(144px);">
                    </ul>
                </div>
                <div class="city_html">
                    <ul id="cityUl"  class="transform_ul" style="transform: translateY(144px);">
                    </ul>
                </div>
                <div class="urban_html">
                    <ul id="urbanUl"  class="transform_ul" style="transform: translateY(144px);">
                    </ul>
                </div>
                <p class="border_p"><span></span><span></span><span></span></p>
            </div>
            <div class="cityBtn">
                <a class="save_btn">确认</a><a class="cancle_btn">取消</a>
            </div>
        </div>
    </div>
    

    style.css

    *{margin: 0;padding: 0;}
        ._layer_big{position: fixed;left:0;top:0;width: 100%;height: 100%;}
        ._layer{position: fixed;left:0;top:0;width: 100%;height: 100%;background-color: rgba(0,0,0,.7);transition:.5s;z-index: 5}
        .layer_box{position: absolute;bottom:0;width:100%;height:360px;transition: .5s;background-color:#ffffff;z-index: 67}
        .cityHtml{width:100%;height: 288px;font-size: 16px;color:#333333;}
        .cityBtn{width:100%;height: 36px;line-height: 36px;text-align: right;margin-top:10px;}
        .cityHtml>div{float:left;width:33%;height: 288px;overflow:hidden;}
        ul{list-style: none;transition:.5s;}
        ul li{text-align:center;height:36px;line-height: 36px;color:#333333;box-sizing: border-box;-webkit-box-sizing:border-box;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;font-size:14px;margin:0 5px;}
        .cityHtml>p{position: absolute;height: 36px;top:144px;width: 100%;left:0;}
        .border_p span{
            box-sizing: border-box;-webkit-box-sizing:border-box;border-top:1px solid red;height:36px;border-bottom:1px solid red;display: inline-block;width:30%;margin:0 4px;
        }
        .cancle_btn,.save_btn{display: inline-block;width:100px;text-align: center;margin-right:10px;height: 100%;line-height: 36px;border:1px solid #999999;border-radius:10px;}
    

    一些简单的JS让其实现弹出的效果。

    document.querySelector("#input").addEventListener("focus",function(){
        document.activeElement.blur()
        document.querySelector("._layer_big").style.display = "block";
    });
    document.querySelector("._layer").addEventListener("touchend",function(e){
        document.querySelector("._layer_big").style.display = "none";
    });
    document.addEventListener("touchstart",function(e){
        e.preventDefault();
    });
    document.querySelector(".cancle_btn").addEventListener("touchend",function(){
        document.querySelector("._layer_big").style.display = "none";
    });
    

    现在我们已经实现了页面布局与基本样式。为了方便获取translateY,直接将样式写入到DOM中,而且引入了一个JS文件,其中存储了所有城市的key和value。在这里有一点非常需要注意的是,input标签聚焦的时候会唤起手机自带的键盘。禁止唤起键盘的方法有很多种,可以使用将input的type属性设为hidden,用span标签替代input标签等,不过在这边是让其失焦以此来达到禁止唤起键盘的效果,具体代码就像这下面写的:

    document.activeElement.blur();
    

    下面我会将效果分成几个步骤进行讲解,尽可能的将制作过程讲解清楚。

    搭建完整的页面

    当input聚焦的时候,就应当完整的将各级城市完整的排序好,并且页面中默认选中北京市,随后出现的是北京下辖的区县。

    //渲染省份,初始的时候默认将第一个省份标记为选中状态
    var proHtml = " ",targetNum = 1;
    for(var i=0;i<addr_arr[0].length;i++){
        if(i==0){
            proHtml += '<li class="current_ul" data-id='+addr_arr[0][i][0]+'>'+addr_arr[0][i][1]+'</li>'
        }else{
            proHtml += '<li data-id='+addr_arr[0][i][0]+'>'+addr_arr[0][i][1]+'</li>';
        }
    }
    document.querySelector("#proUl").innerHTML = proHtml;
    document.querySelector("#proUl").innerHTML = proHtml;
    //渲染城市
    renderCity(targetNum);
    function renderCity(num){
    //渲染城市的时候默认将城市的位置处于初始化的位置,并且默认将第一个城市标记为选中状态
        document.querySelector("#cityUl").style.transform =document.querySelector("#cityUl").style.WebkitTransform = "translateY(144px)";
        var i=0,len = addr_arr[num].length,cityHtml = "",cityArr = addr_arr[num];
        for(i;i<len;i++){
            if(i==0){
                cityHtml += '<li class="current_ul" data-id='+cityArr[i][0]+'>'+cityArr[i][1]+'</li>';
            }else{
                cityHtml += '<li data-id='+cityArr[i][0]+'>'+cityArr[i][1]+'</li>';
            }
        };
        document.querySelector("#cityUl").innerHTML = cityHtml;
        var urbanId = document.querySelector("#cityUl li").getAttribute("data-id");
    //渲染县区
        renderUrban(urbanId);
    };
    function renderUrban(num){
    //渲染县区的时候默认将县区的位置处于初始化的位置,并且默认将第一个县区标记为选中状态
        document.querySelector("#urbanUl").style.transform = document.querySelector("#urbanUl").style.WebkitTransform = "translateY(144px)";
        var i=0,len = addr_arr[num].length,urbanHtml = "",urbanArr = addr_arr[num];
        for(i;i<len;i++){
            if(i==0){
                urbanHtml += '<li class="current_ul" data-id='+urbanArr[i][0]+'>'+urbanArr[i][1]+'</li>'
            }else{
                urbanHtml += '<li data-id='+urbanArr[i][0]+'>'+urbanArr[i][1]+'</li>';
            }
            
        };
        document.querySelector("#urbanUl").innerHTML = urbanHtml;   
    };
    

    写完这些代码,最终的样式框架呈现出如下效果:

    demo.png
    对三个联动器实现滑动的效果

    滑动的原理说白了跟轮播图的原理是一样的,只不过轮播图里图片的切换变成了一个地区的滚动。当然,由于需要获取准确的地区,所以在滚动的时候设定其滚动的距离一定为36px的倍数。其中滑动的效果代码如下:

    var transformUl = document.querySelectorAll(".transform_ul"),proHtml="";
    //对各个地区的区域绑定滑动事件。
    for(var i=0;i<transformUl.length;i++){
        transformUl[i].addEventListener("touchstart",touchStartUl);
        transformUl[i].addEventListener("touchmove",touchMoveUl);
        transformUl[i].addEventListener("touchend",touchEndUl);
    };
    var startY,startTranslateY,touches,cityHtmlHeight = document.querySelector(".cityHtml").getBoundingClientRect().height,ulEle = document.getElementsByClassName("transform_ul"),endY,targetNum=1,ulHeight,currId;
    function touchStartUl(e){
        touches = e.changedTouches[0];
        startY = touches.pageY;
        startTranslateY = parseInt(this.style.transform.split("(")[1]);
    }
    function touchMoveUl(e){
        touches = e.changedTouches[0];
        endY = touches.pageY;
        this.style.transform = this.style.WebkitTransform = "translateY("+(startTranslateY+endY-startY)+"px)";
    }
    function touchEndUl(e){
        ulHeight = this.getBoundingClientRect().height;
        startTranslateY = parseInt(this.style.transform.split("(")[1]);
        startTranslateY = Math.round((startTranslateY/36)) * 36;
        if(startTranslateY>144){
            startTranslateY = 144;
        }
        if(startTranslateY<(180-ulHeight)){
            startTranslateY = 180-ulHeight;
        };
        this.style.transform = this.style.WebkitTransform = "translateY("+startTranslateY+"px)";
    };
    

    将被标记的城市选中输出到input标签中

    //当touchend事件发生的时候,应当标记城市并且当确定好默认选项的时候,将选中的城市输出到input中。
    //所以对touchEndUl事件进行改动
    function touchEndUl(e){
        ulHeight = this.getBoundingClientRect().height;
        startTranslateY = parseInt(this.style.transform.split("(")[1]);
        startTranslateY = Math.round((startTranslateY/36)) * 36;
        if(startTranslateY>144){
            startTranslateY = 144;
        }
        if(startTranslateY<(180-ulHeight)){
            startTranslateY = 180-ulHeight;
        };
        targetNum = Math.abs(startTranslateY/36-5);
        document.getElementsByClassName("cityHtml")[0].dataset[this.id.split("U")[0]] = targetNum;
        this.style.transform = this.style.WebkitTransform = "translateY("+startTranslateY+"px)";
        var aLi = this.getElementsByTagName("li");
        for(var i=0;i<aLi.length;i++){
            aLi[i].className = "";
        }
        this.getElementsByTagName("li")[targetNum-1].className = "current_ul";
        currId = this.getElementsByClassName("current_ul")[0].getAttribute("data-id");
        console.log(currId);
        if(this.id=="cityUl"){
            renderUrban(currId);
        };
        if(this.id=="proUl"){
            renderCity(targetNum);      
        }
    };
    document.querySelector(".cancle_btn").addEventListener("touchend",function(){
        document.querySelector("._layer_big").style.display = "none";
    });
    //保存结果
    document.querySelector(".save_btn").addEventListener("touchend",function(){
        var checkedCity = document.querySelectorAll(".current_ul");
        var checkedHtml = '';
        for(var i=0;i<checkedCity.length;i++){
            if(checkedHtml==""){
                checkedHtml += checkedCity[i].innerHTML;
            }else{
                checkedHtml += '-' + checkedCity[i].innerHTML;
            }
        };
        document.querySelector("#input").value = checkedHtml;
        document.querySelector("._layer_big").style.display = "none";
    })
    

    最终,整体的效果如下

    cityPicker.gif

    到这里代码就基本上完成了,但是项目目前还存在动画效果过于生硬以及面向过程的难维护,效率低,难扩展的问题,而这个也将在一个版本解决。
    城市联动器项目地址

    相关文章

      网友评论

        本文标题:基于面向过程的城市联动器

        本文链接:https://www.haomeiwen.com/subject/lqvvattx.html