在做项目中,一个城市联动器效果吸引了我的注意,虽然在项目中是引入第三方插件实现功能,但是做完项目之后自己还是想自己动手将效果实现。限于自己的水平,目前还是使用面向过程的形式将效果给做出来,后期会对代码进行修改。
页面结构和基础样式
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;
};
写完这些代码,最终的样式框架呈现出如下效果:
![](https://img.haomeiwen.com/i3704151/fdf1e959493be4d5.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";
})
最终,整体的效果如下
![](https://img.haomeiwen.com/i3704151/337f6114eda4cf04.gif)
到这里代码就基本上完成了,但是项目目前还存在动画效果过于生硬以及面向过程的难维护,效率低,难扩展的问题,而这个也将在一个版本解决。
城市联动器项目地址。
网友评论