手机端上下滑动选择项小组件
这是一个手机端的滑动选择小组件。
详细的需求介绍:话费充值,滑动选择充值面额,显示对应的应付金额即可。
重点请看Javascript部分的代码,请大神指点。跪谢!
贴代码~
CSS 部分:
1 html, body, h1, h2, h3, p, dl, dd, ol, ul, th, td, form, fieldset, input, button, textarea, a { 2 margin: 0; 3 padding: 0; } 4 5 html { 6 -webkit-text-size-adjust: none; 7 word-wrap: break-word; 8 -webkit-touch-callout: none; } 9 10 h1, h2, h3 { 11 font-size: 100%; } 12 13 ol, ul { 14 list-style: none; } 15 16 table { 17 border-collapse: collapse; 18 border-spacing: 0; 19 empty-cells: show; 20 font-size: inherit; } 21 22 fieldset, img { 23 border: 0; } 24 25 cite, em, s, i, b { 26 font-style: normal; } 27 28 input, button, textarea, select { 29 font-size: 100%; } 30 31 body, input, button, textarea, select, option, optgroup { 32 font-size: 14px; } 33 34 a, input, textarea { 35 text-decoration: none; 36 outline: 0 none; 37 resize: none; 38 -webkit-tap-highlight-color: transparent; } 39 40 li, img, label, input { 41 vertical-align: middle; } 42 43 img { 44 display: block; } 45 46 a { 47 text-decoration: none; } 48 49 body { 50 font: 14px/1.2 "Microsoft Yahei","Hiragino Sans GB",arial,sans-serif; } 51 52 .clearfix:after { 53 display: block; 54 content: "\20"; 55 height: 0; 56 clear: both; 57 overflow: hidden; 58 visibility: hidden; } 59 60 html, body { 61 height: 100%; 62 background-color: #f1f1f1; } 63 64 /* sprite icon */ 65 .icon-lottery, 66 .icon-phone, 67 .icon-game, 68 .icon-qq, 69 .icon-hotel, 70 .icon-plane, 71 .icon-gas { 72 display: inline-block; 73 background: url(../img/base/sprite-icon.png) 0 0 no-repeat; 74 height: 112px; } 75 76 /* header */ 77 .header { 78 position: fixed; 79 top: 0; 80 left: 0; 81 width: 100%; 82 height: 42px; 83 line-height: 42px; 84 background-color: #f16b50; } 85 86 .header .title { 87 font-size: 16px; 88 color: #fff; 89 text-align: center; } 90 91 .header .icon-code-left { 92 position: absolute; 93 left: 0; 94 top: 0; 95 width: 34px; 96 height: 42px; 97 line-height: 42px; 98 font-size: 16px; 99 color: #fff; 100 font-weight: bold; 101 text-align: center; } 102 103 .wrapper { 104 padding-top: 42px; } 105 106 /* btn */ 107 .btn { 108 display: inline-block; 109 background-color: #f16b50; 110 text-align: center; 111 color: #fff; } 112 113 .btn-large { 114 width: 100%; 115 height: 40px; 116 line-height: 40px; } 117 118 /* home index */ 119 .icon-lottery { 120 background-position: 0 0; 121 width: 108px; } 122 123 .icon-phone { 124 background-position: -108px 0; 125 width: 94px; } 126 127 .icon-game { 128 background-position: -204px 0; 129 width: 108px; } 130 131 .icon-qq { 132 background-position: -314px 0; 133 width: 99px; } 134 135 .icon-hotel { 136 background-position: -415px 0; 137 width: 90px; } 138 139 .icon-plane { 140 background-position: -507px 0; 141 width: 108px; } 142 143 .icon-gas { 144 background-position: -618px 0; 145 width: 81px; } 146 147 .recharge-list .r-l-item { 148 float: left; 149 width: 50%; 150 line-height: 1; 151 text-align: center; } 152 153 .recharge-list a { 154 display: block; 155 padding: 58px 0; } 156 157 .recharge-list .r-l-item.grey { 158 background-color: #eee; } 159 160 .recharge-list .r-l-describe { 161 height: 22px; 162 line-height: 22px; 163 text-align: center; 164 color: #333; } 165 166 /* list.html */ 167 .w-list { 168 xxpadding: 0 22px 0 12px; } 169 170 .w-list .w-l-index { 171 position: fixed; 172 top: 50%; 173 right: 0; 174 margin-top: -196px; 175 width: 22px; 176 text-align: center; } 177 178 .w-list .w-l-index a { 179 color: #666; } 180 181 .w-list .w-l-content dt { 182 border-bottom: 1px solid #ccc; 183 padding: 10px; 184 line-height: 1.2; 185 background-color: #f4f4f4; 186 font-size: 16px; 187 color: #f16b50; } 188 189 .w-list .w-l-content dd { 190 border-bottom: 1px solid #ccc; 191 padding: 16px; 192 line-height: 1.4; } 193 194 .w-list .w-l-content dd a { 195 font-size: 12px; 196 color: #333; } 197 198 /* recharge.html */ 199 .box-radius { 200 margin: 14px 20px 0; 201 border: 1px solid #ccc; 202 border-radius: 8px; 203 background-color: #FFF; 204 overflow: hidden; } 205 206 .input-group .label, 207 .input-group .input-control { 208 display: block; 209 padding: 12px 8px; } 210 211 .input-group .label { 212 line-height: 22px; } 213 214 .input-group .input-control { 215 border: 0; 216 line-height: 22px; 217 width: 100%; 218 box-sizing: border-box; 219 background-color: transparent; } 220 221 .input-group input.input-control { 222 border: 1px solid #fff; 223 border-top: 1px solid #ccc; } 224 225 .input-group input.input-control:first-child { 226 margin-top: -1px; } 227 228 .input-group input.input-control.error { 229 border-color: #f16b50; 230 border-radius: 7px; } 231 232 .recharge .btn-area { 233 margin: 14px 20px 20px; } 234 235 .recharge-list { 236 border-top: 1px solid #ccc; 237 height: 128px; 238 overflow: hidden; } 239 240 .recharge-list .r-l-left { 241 position: relative; 242 float: left; 243 border-right: 1px solid #ccc; 244 width: 50%; 245 height: 100%; 246 xxoverflow: auto; } 247 248 .recharge-list .r-l-right { 249 float: right; 250 padding: 46px 14px 0; 251 line-height: 1.5; 252 font-weight: bold; 253 width: 49%; 254 box-sizing: border-box; } 255 256 .recharge-list .r-l-left ul { 257 margin: 0 12px; 258 -webkit-transition: -webkit-transform 0s ease-out; 259 -webkit-transform: translate3d(0, 0, 0); } 260 261 .recharge-list .item { 262 border-top: 1px solid #ccc; 263 padding: 0 6px; 264 height: 42px; 265 line-height: 42px; } 266 267 .recharge-list .item.current { 268 font-weight: bold; 269 font-size: 15px; } 270 271 .recharge-list .item:first-child { 272 border: 0; } 273 274 .top-cover, 275 .bottom-cover { 276 position: absolute; 277 left: 0; 278 width: 100%; 279 height: 22px; 280 text-indent: -999em; 281 background: #fff; 282 opacity: 0.5; } 283 284 .top-cover { 285 top: 0; } 286 287 .bottom-cover { 288 bottom: 0; } 289 290 .skin-color { 291 font-weight: bold; 292 color: #f16b50; } 293 294 @media screen and (max-width: 320px) { 295 .icon-lottery, 296 .icon-phone, 297 .icon-game, 298 .icon-qq, 299 .icon-hotel, 300 .icon-plane, 301 .icon-gas { 302 height: 80px; 303 background-size: 460px; } 304 305 .icon-lottery { 306 background-position: 0 0; 307 width: 71px; } 308 309 .icon-phone { 310 background-position: -71px 0; 311 width: 62px; } 312 313 .icon-game { 314 background-position: -133px 0; 315 width: 73px; } 316 317 .icon-qq { 318 background-position: -206px 0; 319 width: 66px; } 320 321 .icon-hotel { 322 background-position: -271px 0; 323 width: 62px; } 324 325 .icon-plane { 326 background-position: -333px 0; 327 width: 71px; } 328 329 .icon-gas { 330 background-position: -406px 0; 331 width: 53px; } }
HTML 结构部分:
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"/> 5 <meta name="viewport" content="width=device-width, initial-scale=1,target-densitydpi=medium-dpi, user-scalable=no"> 6 <meta name="format-detection" content="telephone=no" /> 7 <title>充值首页 -- 手机充值</title> 8 <link rel="stylesheet" href="css/index.css" /> 9 </head> 10 <body> 11 12 <header class="header"> 13 <h2 class="title">手机充值</h2><span class="icon-code-left">〈</span> 14 </header> 15 16 <div class="wrapper recharge"> 17 <div class="box-radius input-group"> 18 <label class="label">充值类型</label> 19 <input class="input-control" placeholder="请输入手机号码"/> 20 </div> 21 <div class="box-radius input-group"> 22 <label class="label">充值面额</label> 23 <div class="recharge-list clearfix"> 24 <div class="r-l-left"> 25 <s class="top-cover">top cover</s> 26 <s class="bottom-cover">bottom cover</s> 27 <div id="sliderEl" style="-webkit-transform: translate3d(0, 0, 0);"></div> 28 </div> 29 <div class="r-l-right"> 30 <span class="title">售价</span> 31 <p class="price">¥ <i id="showPriceEl"></i></p> 32 </div> 33 </div> 34 </div> 35 <div class="box-radius input-group"> 36 <label class="label">应付金额:<span class="skin-color">¥<i id="priceEl"></i></span></label> 37 </div> 38 <div class="btn-area"><em class="btn btn-large">立刻充值</em></div> 39 </div> 40 <script type="text/javascript" src="js/scroll-list.js"></script> 41 <script> 42 (function(){ 43 44 //调用 45 var data = [{id:"bm_50",showPrice:50,price:49.750}, 46 {id:"bm_30",showPrice:30,price:29.850}, 47 {id:"bm_100",showPrice:100,price:99.500}, 48 {id:"om_300",showPrice:300,price:298.500}, 49 {id:"om_500",showPrice:500,price:497.500}], 50 el = document.getElementById('sliderEl'); 51 52 var slider = new Slider({ 53 dataModule : data, 54 sliderEl : el, 55 current : 1, 56 callBack : function(data){ 57 if (!data) { return; } 58 data.showPrice && (document.getElementById('showPriceEl').innerHTML = data.showPrice); 59 data.price && (document.getElementById('priceEl').innerHTML = data.price); 60 } 61 }); 62 63 })(); 64 </script> 65 </body> 66 </html>
Javascript 部分:
1 var Slider; 2 (function(){ 3 4 Slider = function(opt){ 5 this.el = opt.sliderEl; //模板容器 6 this.idx = opt.current; //设定初始索引 7 this.callBack = opt.callBack; 8 9 this.dataIdx = []; //缓存索引数据 10 this.dataModule = {}; //缓存数据对象 11 this.setDataModule(opt.dataModule); 12 13 //初始化 14 this.init(); 15 }; 16 17 //数据处理 18 Slider.prototype.setDataModule = function(data){ 19 if (!data) return; 20 for (var i = 0, len = data.length; i < len; i++) { 21 var d = data[i]; 22 if (!d) { continue; } 23 this.dataIdx.push( d.id ); 24 this.dataModule[ d.id ] = d; 25 } 26 }; 27 28 Slider.prototype.init = function(){ 29 //渲染模板 30 this.renderTpl(); 31 32 //设定滚动高度 33 this.scaleH = this.el.getElementsByTagName('li')[0].clientHeight + 1; //1px border 34 35 //初始总偏移量 36 this.scrollY = 0; 37 38 //绑定事件 39 this.bindEvent(); 40 41 }; 42 43 //渲染模板 44 Slider.prototype.renderTpl = function(){ 45 var data = this.dataIdx, 46 el = this.el, 47 dataArr = ['<ul>']; 48 for (var i = 0, len = data.length; i < len; i++) { 49 var id = data[i], 50 d = this.dataModule[id]; 51 if (!d) continue; 52 dataArr.push( this.setTpl(d) ); 53 } 54 dataArr.push('</ul>') 55 el.innerHTML = dataArr.join(''); 56 57 //模板渲染完成后,设定初始选定项 58 el.getElementsByTagName('li')[ this.idx ].className = 'item current'; 59 var cid = data[this.idx], 60 cdata = this.dataModule[cid]; 61 this.callBack(cdata); 62 }; 63 64 //普通list模板 65 Slider.prototype.setTpl = function(data){ 66 return '<li class="item" data-val="'+ data.id +'">¥ '+ data.showPrice +'</li>'; 67 }; 68 69 70 //绑定事件 71 Slider.prototype.bindEvent = function(){ 72 73 var self = this, 74 el = this.el, 75 76 //手指按下 77 startHandler = function(event){ 78 //记录手指按下的坐标 79 self.startY = event.touches[0].pageY; 80 //清除偏移量 81 self.offsetY = 0; 82 //事件对象 83 self.target = event.target; 84 85 }, 86 //手指移动 87 moveHandler = function(event){ 88 event.preventDefault(); 89 90 //计算手指的偏移量 91 self.offsetY = event.targetTouches[0].pageY - self.startY; 92 93 //当前偏移量 + 总偏移量 94 var scrollHeight = self.offsetY + self.scrollY; 95 96 el.style.webkitTransform = 'translate3d(0, '+ scrollHeight +'px, 0)'; 97 98 }, 99 //手指抬起 100 endHandler = function(event){ 101 event.preventDefault(); 102 103 //记录滑动边界,用于判定滑动的类型 104 var boundary = self.scaleH / 2; 105 106 if (self.offsetY > boundary) { 107 self.setIndex('-1'); 108 } else if (self.offsetY < 0 && self.offsetY < -boundary) { 109 self.setIndex('+1'); 110 } else { 111 self.setIndex('0'); 112 } 113 114 }; 115 116 //绑定事件 117 el.addEventListener('touchstart', startHandler); 118 el.addEventListener('touchmove', moveHandler); 119 el.addEventListener('touchend', endHandler); 120 }; 121 122 //滑动 123 Slider.prototype.setIndex = function(n){ 124 var el = this.el; 125 126 //情景一:偏移量太小,偏移回原始位置即可 127 if (n == '0'){ 128 el.style.webkitTransform = 'translate3d(0, '+ this.scrollY +'px, 0)'; 129 return; 130 } 131 132 //情景二:依照滚动的间距计算 133 var liEl = el.getElementsByTagName('li'), 134 liLen = liEl.length; 135 //在改变 this.idx 之前要去掉 当前 idx 的 current 状态 136 liEl[this.idx].className = 'item'; 137 138 var maxY = this.scaleH * -(liLen - 2), 139 minY = this.scaleH, 140 boundary = this.scaleH / 2, 141 d = Math.abs( this.offsetY % this.scaleH ), //取模 -- 偏移量 142 offIdx = Math.abs( this.offsetY / this.scaleH ); //取值 -- 偏移量 143 //四舍五入取 idx 144 if (d > boundary) { 145 offIdx = Math.ceil(offIdx); 146 } else { 147 offIdx = Math.floor(offIdx); 148 } 149 this.idx = this.idx + (n * offIdx); //重置 idx 索引 150 151 if (n == '+1'){ 152 scrollHeight = (-n * this.idx) * this.scaleH + (n * this.scaleH); 153 } else { 154 scrollHeight = (n * this.idx) * this.scaleH - (n * this.scaleH); 155 } 156 157 if (this.idx < 0) { 158 scrollHeight = minY; 159 this.idx = 0; 160 } else if (this.idx >= liLen) { 161 scrollHeight = maxY; 162 this.idx = liLen - 1; 163 } 164 165 this.scrollY = scrollHeight; 166 167 var li = liEl[this.idx], 168 cId = li.getAttribute('data-val'), 169 currentItemData = this.dataModule[cId]; 170 li.className = 'item current'; 171 172 el.style.webkitTransform = 'translate3d(0, '+ scrollHeight +'px, 0)'; 173 174 this.callBack(currentItemData); 175 }; 176 177 })();