Web前端之路让前端飞饥人谷技术博客

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

2017-04-14  本文已影响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

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

上一篇下一篇

猜你喜欢

热点阅读