web app 基础界面框架搭建

一、问题的产生

基础框架界面如图1所示,目前app流行布局

技术分享

      图1

布局关注点:

  1.Title和Footer部分为固定布局position:fixed,存在的争议是android2.x和IOS4一下说是不支持,过渡办法是position:absolute(本手机亲测android2.3.7是支持position:fixed的,android2.2系列市场占有率不到1%,IOS4一下也是忽略,如果要兼容js控制位置也可以...自行百度)

  2.Content内容为滚动,其实通过布局为position:absolute;overflow:auto;能达到支持滚动条、动态滑动等效果,存在争议的是android2.x和IOS5不支持overflow:auto,所以必须要js模拟滑动效果(由此引出下文)

 

二、问题的解决

解决的思路如图2所示

技术分享

解决问题需要注意的点:

1.js判断操作系统类型,根据navigator.userAgent的返回基本可以判断android类型(具体方法见代码,IOS网上说也可以,但是我是没有苹果设备没来得及测试)

2.系统默认滚动条的添加需要采用动态创建style标签,往标签里写webkit的自定义滚动条样式,类似"::-webkit-scrollbar {width: 3px;}",这种样式可以达到修改浏览器默认样式的效果,主要是为了跟自定义滚动条样式统一(具体有哪些见代码)

3.Content部分滑动效果采用css的transition,android都是采用webkit内核,低版本需要加前缀-webkit-,要达到的效果有界面跟随手指滑动、自定义滚动条滚动、快速滑动后的阻尼效果、上下边界超出部分的回滚等,其中浏览器原生效果没有上下边界超出部分回滚效果,这个可以自己做

三、代码和分析

1.html部分

 1 .header2-bar
 2     span.header-back 返回
 3     span.head-title 标题
 4 .content2-bar#wrapper
 5     .content-scroll#scroll
 6         ul
 7             li 周考
 8             li 月考
 9             li 季考
10             li 期考
11             li 年考
12             li 年考1
13             li 年考2
14             li 年考3
15             li 年考4
16             li 年考5
17             li 年考6
18             li 年考7
19             li 年考8
20             li 年考9
21             li 年考10
22             li 年考11
23             li 年考12
24             li 年考13
25             li 年考14
26             li 年考15
27             li 年考16
28             li 年考17
29             li 年考18
30             li 年考19
31             li 年考20
32             li 年考21
33             li 年考22
34             li 年考23
35             li 年考24
36             li 年考25
37             li 年考26
38             li 年考27
39             li 年考28
40             li 年考29
41             li 年考30
42             li 年考31
43             li 年考32
44             li 年考33
45 .footer2-bar
46     ul.footer-nav
47         li 主页
48         li 数学
49         li 语文
50         li 物理
51         li 化学

由于开发框架的原因,我这里提供的是jade的源代码,就跟html一样(最后也是编译为html),还不明白的话还是自行百度

2.css部分

 1 .header2-bar {
 2   position: fixed;
 3   width: 100%;
 4   height: 50px;
 5   z-index: 2;
 6   left: 0;
 7   top: 0;
 8   text-align: center;
 9   line-height: 50px;
10 
11   .header-back {
12     display: block;
13     width: 30px;
14     height: 30px;
15     position: absolute;
16     left: 10px;
17     top: 10px;
18     padding: 3px;
19     line-height: 30px;
20   }
21 
22 }
23 .content2-bar {
24   position: absolute;
25   width: 100%;
26   top: 50px;
27   left: 0;
28   bottom: 50px;
29   z-index: 1;
30   overflow-y: auto;
31 
32   .content-scroll {
33     overflow: auto;
34     position: relative;
35 
36     li {
37       height: 45px;
38       line-height: 45px;
39     }
40   }
41 
42   .lowerScrollBar {
43     position: absolute;
44     right: 0;
45     top: 0;
46     width: 3px;
47     background-color: rgba(0, 0, 0, 0.5);
48     border: 1px solid rgba(255, 255, 255, 0.9);
49     border-radius: 3px;
50   }
51 }
52 .footer2-bar {
53   position: fixed;
54   width: 100%;
55   height: 50px;
56   z-index: 2;
57   left: 0;
58   bottom: 0;
59 
60   .footer-nav {
61     display: block;
62     width: 100%;
63     height: 100%;
64 
65     li {
66       width: 20%;
67       height: 100%;
68       display: block;
69       float: left;
70       line-height: 50px;
71       text-align: center;
72     }
73   }
74 }

由于开发框架的原因,我这里提供的是less的源代码,就跟css一样(最后也是编译为css),还不明白的话还是自行百度

3.js代码

  1 /**
  2          * 低版本Android模拟滑动
  3          * @param id 滑动区域id
  4          */
  5         function noBarsOnTouchScreen(id) {
  6             var elem, //滑动元素
  7                 ty,   //滑动Y位置
  8                 tyStart, //初始滑动Y位置
  9                 scrollBar, //模拟滚动条
 10                 elemHeight, //滑动元素高度
 11                 elemParentHeight, //滑动元素夫容器高度
 12                 startTime, //滑动开始时间
 13                 endTime; //滑动结束时间
 14             if(‘ontouchstart‘ in document.documentElement) {
 15                 if(elem = document.getElementById(id)) {
 16                     elem.style.overflow = ‘hidden‘;
 17                     elemHeight = elem.offsetHeight;
 18                     elemParentHeight = elem.parentNode.offsetHeight;
 19 
 20                     elem.ontouchstart = ts;
 21                     elem.ontouchmove = tm;
 22                     elem.ontouchend = te;
 23 
 24                     addScrollBar();
 25                 }
 26             }
 27 
 28             //touchstart
 29             function ts(e) {
 30                 var tch;
 31                 if(e.touches.length == 1) {
 32                     e.preventDefault();
 33                     e.stopPropagation();
 34                     tch = e.touches[0];
 35                     ty = tch.pageY;
 36                     tyStart = ty;
 37                     startTime = new Date().getTime();
 38 
 39                     this.style.transition = ‘‘;
 40                     scrollBar.style.transition = ‘‘;
 41                 }
 42             }
 43 
 44             //touchmove
 45             function tm(e) {
 46                 var tch;
 47 
 48                 if(e.touches.length == 1) {
 49                     e.preventDefault();
 50                     e.stopPropagation();
 51                     tch = e.touches[0];
 52 
 53                     var top = this.style.top;
 54                     if(top) {
 55                         top = parseInt(top.replace(‘px‘, ‘‘));
 56                     } else {
 57                         top = 0;
 58                     }
 59                     top -= (ty - tch.pageY);
 60                     ty = tch.pageY;
 61 
 62                     //滚动条位置
 63                     var scrollBarTop = (-top) * (elemParentHeight) / elemHeight;
 64 
 65                     if(top > 0) { //保证顶部不拉出
 66                         if(top > 50) top = 50;
 67                     }
 68                     if(top < (elem.parentNode.offsetHeight - elem.offsetHeight) - 50) { //保证底部不拉出
 69                         top = elem.parentNode.offsetHeight - elem.offsetHeight -50;
 70                     }
 71                     console.log(‘top: ‘ + top);
 72                     this.style.top = top + ‘px‘;
 73 
 74 
 75                     scrollBar.style.top = scrollBarTop + ‘px‘;
 76                 }
 77             }
 78 
 79             //touchend
 80             function te(e) {
 81                 e.preventDefault();
 82                 e.stopPropagation();
 83                 endTime = new Date().getTime();
 84                 var scrollBarTop;
 85 
 86                 var top = this.style.top;
 87                 if(top) {
 88                     top = parseInt(top.replace(‘px‘, ‘‘));
 89                 } else {
 90                     top = 0;
 91                 }
 92 
 93                 var speed = Math.abs(top) / (endTime - startTime);
 94                 console.log(speed);
 95                 if(speed > 1.0) {
 96                     if(tyStart < ty) {
 97                         top += speed * 100;
 98                     }
 99                     if(tyStart > ty) {
100                         top -= speed * 100;
101                     }
102                     this.style.transition = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
103                     this.style[‘-webkit-transition‘] = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
104                     this.style.top = top + ‘px‘;
105 
106                     //滚动条位置
107                     scrollBarTop = (-top) * (elemParentHeight) / elemHeight;
108                     scrollBar.style.transition = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
109                     scrollBar.style[‘-webkit-transition‘] = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
110                     scrollBar.style.top = scrollBarTop + ‘px‘;
111                 }
112 
113                 //顶部拉过了 弹回
114                 while(top > 0) {
115                     top = 0;
116 
117                     this.style.transition = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
118                     this.style[‘-webkit-transition‘] = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
119                     this.style.top = top + ‘px‘;
120 
121                     //滚动条位置
122                     scrollBarTop = (-top) * (elemParentHeight) / elemHeight;
123                     scrollBar.style.transition = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
124                     scrollBar.style[‘-webkit-transition‘] = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
125                     scrollBar.style.top = scrollBarTop + ‘px‘;
126                 }
127                 //底部拉过了 弹回
128                 while(top < 0 && (elemParentHeight + Math.abs(top)) > elemHeight) {
129                     top = elemParentHeight - elemHeight;
130 
131                     this.style.transition = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
132                     this.style[‘-webkit-transition‘] = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
133                     this.style.top = top + ‘px‘;
134 
135                     //滚动条位置
136                     scrollBarTop = (-top) * (elemParentHeight) / elemHeight;
137                     scrollBar.style.transition = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
138                     scrollBar.style[‘-webkit-transition‘] = ‘top 0.4s cubic-bezier(.12,.71,.83,.67)‘;
139                     scrollBar.style.top = scrollBarTop + ‘px‘;
140                 }
141 
142 
143 
144             }
145 
146             //添加默认滚动条
147             function addScrollBar() {
148                 scrollBar = document.createElement(‘div‘);
149                 scrollBar.className = ‘lowerScrollBar‘;
150                 scrollBar.id = ‘lowerScrollBar‘;
151                 scrollBar.style.height = elemParentHeight * elemParentHeight / elemHeight + ‘px‘;
152                 elem.parentNode.appendChild(scrollBar);
153             }
154         }
155 
156         /**
157          * 添加默认滚动条
158          */
159         function addDefultScrollBar() {
160             var style = document.createElement(‘style‘);
161             style.type = ‘text/css‘;
162             style.innerHTML = ‘::-webkit-scrollbar {width: 3px;}::-webkit-scrollbar-thumb {background-color: rgba(0, 0, 0, 0.5);border: 1px solid rgba(255, 255, 255, 0.9);border-radius: 3px;}::-webkit-scrollbar-thumb:window-inactive {background-color: rgba(0, 0, 0, 0.6);}‘;
163             document.head.appendChild(style);
164         }
165 
166 
167         /**
168          * 判定是否是低版本android
169          * 是低版本android就要模拟滑动
170          * 不是低版本android就要添加默认滚动条
171          */
172         function isLowerAndroid() {
173             var us = navigator.userAgent.toLowerCase();
174             var isAndroid = us.indexOf(‘android‘) > -1;
175             if(isAndroid) {
176                 var version = us.substr(us.indexOf(‘android‘) + 8, 3);
177                 if(parseInt(version) < 4) {
178                     noBarsOnTouchScreen(‘scroll‘);
179                 } else {
180                     addDefultScrollBar();
181                 }
182             } else {
183                 addDefultScrollBar();
184             }
185         }
186 
187         isLowerAndroid();

红色字体不是突出,只是粘贴的时候富文本控件自己添加的

四、结果分析

1.测试在android2.3.7(好几年前的手机了...)和android4.4.4里测试都通过,基本上还是能模拟原生浏览器的下拉效果,更复杂和更棒的滑动效果就要自己动手了

2.界面效果如图3所示

技术分享

3.进一步分析存在的问题

  1)手势判断,目前判断手指滑动的方向是ontouchstart的时候记录手指的位置,ontouchmove的时候更新手指位置,在ontouchend的时候对差值计算判断手指的上下滑动方向,这里存在一个问题连续多次在屏幕上滑动就会不靠谱,理想的办法是分析最后多次ontouchmove的手指位置变化趋势,根据这个趋势来判定手指滑动的方向(不知道有些库是怎么判断的,如果知道的人可以分享一下)

  2)阻尼效果,快速滑动屏幕的时候有一个阻尼效果,会使得屏幕往手指滑动方向再滑动一定距离,这个需要判定手指滑动的速度。目前采用的是在ontouchstart记录开始时间,ontouchend记录结束时间,根据滑动具体计算平均滑动速度,如果速度值大于1,表示需要产生阻尼滑动效果,然后再根据该速度滑动一定距离,这个效果没有原生效果看起来那么棒(这里也求一个阻尼效果的js分析)

  3)css动画和js动画,之前一直采用js做动画,发现都达不到平滑过渡的效果(js太差劲了),不得已采用css模式,js厉害的人可以分析一下采用原生代码完成这个滑动效果,我想的话最好是支持设定贝塞尔值的滑动

  4)这只是一个主界面架子,app还要支持横向滚动,界面切换,局部切换...太多东西了

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。