zoukankan      html  css  js  c++  java
  • webapp应用--模拟电子书翻页效果

    前言:

         现在移动互联网发展火热,手机上网的用户越来越多,甚至大有超过pc访问的趋势。所以,用web程序做出仿原生效果的移动应用,也变得越来越流行了。这种程序也就是我们常说的单页应用程序,它也有一个英文缩写,叫SPA; 它最大的特点就是可以利用前端技术做出跨平台的移动应用。技术难点在于理解虚拟页面与物理页面之间的变换关系。一个偶然的机会,我由php程序员转为web前端开发,主攻javascript编程,不知不觉,已经快两年了。一直有一种想写一个webapp应用框架的冲动,但是各种原因,终究没有付出实践。于是打算从做一个简单的webapp应用开始,万事开头难,今天就搭一个简单的界面。

       

    HTML代码:

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>单页应用</title>
      <link rel="stylesheet" href="css/common.css" type="text/css"/>
    </head>
    <body>
    <div class="container"> 
      <header>
        <h3>sameple test </h3>
      </header>
      <ul class="root">
        <li class="page">1</li>
        <li class="page">2</li>
        <li class="page">3</li>
        <li class="page">4</li>
        <li class="page">5</li>
        <li class="page">6</li>
        <li class="page">7</li>
        <li class="page">8</li>
        <li class="page">9</li>
        <li class="page">10</li>
      </ul>
      <div class="left">prev</div>
      <div class="right">next</div>
      <footer>
        <h4>(c)2015 by ouyangli</h4> 
      </footer>
    </div>
    </body>
    <script type="text/javascript" src="lib/core.js"></script>
    </html>
    View Code

    css:

     1 ul , li {
     2     margin: 0;
     3     padding: 0;
     4     list-style: none;
     5 }
     6 h3,h4,p {
     7     margin:0;
     8     padding: 0;
     9 }
    10 header {
    11     position: absolute;
    12     width:100%;
    13     top:0;
    14     left: 0;
    15     z-index: 9;
    16 }
    17 
    18 header h3 {
    19     text-align: center;
    20     height: 3em;
    21     line-height: 3em;
    22     border-bottom: 1px solid green;
    23 }
    24 
    25 .container {
    26     position: absolute;
    27     width :320px;
    28     height: 480px;
    29     left:320px;
    30     top:2em;
    31 }
    32 
    33 .root {
    34     position: absolute;
    35     width :100%;
    36     height: 100%;
    37     top :0;
    38     left:0;
    39     overflow:hidden;
    40     -webkit-perspective:1000;
    41     -webkit-user-select: none;
    42     -webkit-transform-style:preserve-3d;
    43 }
    44 
    45 .page {
    46     position: absolute;
    47     width: 318px;
    48     height: 100%;
    49     overflow: hidden;
    50     border:1px solid green;
    51 }
    52 
    53 .left {
    54     left :2px;
    55 }
    56 .right {
    57     right:2px;
    58 }
    59 
    60 .left,.right {
    61     position: absolute;
    62     top:45%;
    63     width:3em;
    64     height: 3em;
    65     line-height: 3em;
    66     text-align: center;
    67     border-radius: 15%;
    68     border:1px dashed blue;
    69 }
    70 
    71 .left:hover,.right:hover {
    72     background-color: #33ff44;
    73     cursor:pointer;
    74 }
    75 
    76 footer {
    77     position: absolute;
    78     width: 100%;
    79     bottom: 0;
    80 }
    81 
    82 h4 {
    83     height: 3em;
    84     line-height: 3em;
    85     text-align: center;
    86     border-top: 1px solid green;
    87 }
    View Code

     以上源码将会在页底提供打包下载,这里贴出中间过程,只是想让大家能明白,我是怎么一步一步把这个程序写出来的。如果有疑问的地方就给我留言好了,我会尽量回复。

    演示地址:http://runjs.cn/detail/o4ql6f6a

    细心的话,你会发现左上角有一个“乱码”,其实那是因为所有的页面都堆叠在一起,造成页数看不清了。这正是我们接下来要解决的问题之一。

    js:

     1 //初始化
     2 ;(function(){
     3     var pages = document.querySelectorAll('li');
     4     var width = 320;
     5     var len = pages.length;
     6     var setpost = function(element){
     7         element.style.transform = 'translate3d('+width+'px, 0, 0)';
     8     }
     9 
    10     //把除1以外的页堆在右边
    11     while(--len){
    12         setpost(pages[len]);
    13     }
    14 }());
    View Code

    好了,现在看起来仅管还是很丑,但是至少已经符合我预期的样子了。在这里我把第一页已外的页面全部摆到了屏幕的右边,你看不到它们。这样做的目的是要模拟手机上的翻页效果。接下来,就要实现这个非常令人期待的滑动翻页效果。现在该javascript发挥威力的时候了。这是一个简单的应用,我尽量把所有的js写在core.js中,并采用最普通的函数式编程。

     1 //初始化
     2 ;(function(){
     3     var pages = document.querySelectorAll('li');
     4     var width = 320;
     5     var len = pages.length;
     6     var setpost = function(element){
     7         element.style.transform = 'translate3d('+width+'px, 0, 0)';
     8     }
     9 
    10     //把除1以外的页堆在右边
    11     while(--len){
    12         setpost(pages[len]);
    13     }
    14 }());
    15 
    16 //控制逻辑
    17 ;(function(){
    18     //这里直接使用了新的api,因为移动应用可以这样任性。
    19     var pages = document.querySelectorAll('li');
    20     //左右翻页按钮
    21     var left = document.querySelector('.left');
    22     var right = document.querySelector('.right');
    23     //取得所有的子页面
    24     var pagesLen = pages.length-1;
    25 
    26     //标记当前页面
    27     var currIndex = 0;
    28 
    29     //移动页面
    30     var move = function(index,pos){
    31         var width = 320 * pos;
    32         var page = pages[index];
    33         page.style.transform = 'translate3d('+width+'px, 0, 0)';
    34     }
    35 
    36     //向左翻页
    37     var toLeft = function(){
    38 
    39         if(currIndex!==pagesLen){
    40             move(currIndex,-1);
    41             move(++currIndex,0);
    42         } 
    43     }
    44 
    45     //向右翻页
    46     var toRight = function(){
    47         if(currIndex!==0){
    48             move(currIndex,1);
    49             move(--currIndex,0);
    50         }
    51     }
    52 
    53     //监听动作
    54     left.onclick = function(){
    55         toLeft();
    56     }
    57 
    58     right.onclick = function(){
    59         toRight();
    60     }
    61 
    62 }())
    View Code

    现在我们的程序可以左右翻页了,不过除了页码发生了变化之外,用户好像感觉不到有翻页的效果。左右翻页按钮的作用与我们期望的效果不一致,所以初始化时,把页面堆在右边其实是不太好的,改为左边会更好一些。嗯,还要继续完善。

     1 //初始化
     2 ;(function(){
     3     var pages = document.querySelectorAll('li');
     4     var width = 320;
     5     var len = pages.length;
     6     var setpost = function(element){
     7         element.style.transform = 'translate3d(-'+width+'px, 0, 0)';
     8     }
     9 
    10     //把除1以外的页堆在左边
    11     while(--len){
    12         setpost(pages[len]);
    13     }
    14 }());
    15 
    16 //控制逻辑
    17 ;(function(){
    18     //这里直接使用了新的api,因为移动应用可以这样任性。
    19     var pages = document.querySelectorAll('li');
    20     //左右翻页按钮
    21     var left = document.querySelector('.left');
    22     var right = document.querySelector('.right');
    23     //取得所有的子页面
    24     var pagesLen = pages.length-1;
    25 
    26     //标记当前页面
    27     var currIndex = 0;
    28 
    29     //移动页面
    30     var move = function(index,pos){
    31         var width = 320 * pos;
    32         var page = pages[index];
    33         page.style.transform = 'translate3d('+width+'px, 0, 0)';
    34         page.style.transitionDuration = '300ms';
    35     }
    36 
    37     //上一页
    38     var toLeft = function(){
    39         if(currIndex >0){
    40             move(currIndex,-1);
    41             move(--currIndex,0);
    42         } 
    43     }
    44 
    45     //下一页
    46     var toRight = function(){
    47         if(currIndex < pagesLen){
    48             move(currIndex,1);
    49             move(++currIndex,0);
    50         }
    51     }
    52 
    53     //监听动作
    54     left.onclick = function(){
    55         toLeft();
    56     }
    57 
    58     right.onclick = function(){
    59         toRight();
    60     }
    61 
    62 }())
    View Code

    现在终于看起来像是在翻页的样子了,不过呢,我们在手机上操作的时候,是可以滑动翻页的,这个效果自然也要支持才行。我们先在pc上用鼠标拖动来模拟这一个过程,等逻辑跑通后,再加入触摸事件即可。

     

    继续下一步之前,我们先小结一下:

    首先在初始化代码时,顺道把所有不可见的页面全部堆叠到左边,放在左边的原因是因为我们习惯右边的按钮作为下一页,所以这样摆放,实现起来最简单,我当然要选择最有利于我编码的方式来摆放啦。其次呢,每一次翻页,其实都要移动两个页面。拿点击下一页按钮来说,首先要把当前页移动到不可见的屏幕右边,然后把上一页移到屏幕中间来。上一页的过程正好与之相反。我们发现,点击翻页和滑动翻页,其实都会调用相同的功能。所以在这里可先抽出公共方法,方便代码复用。

    接下来,我们实现鼠标拖动翻页的效果。

     1 //初始化
     2 ;(function(){
     3     var pages = document.querySelectorAll('li');
     4     var width = 320;
     5     var len = pages.length;
     6     var setpost = function(element){
     7         element.style.transform = 'translate3d(-'+width+'px, 0, 0)';
     8     }
     9 
    10     //把除1以外的页堆在左边
    11     while(--len){
    12         setpost(pages[len]);
    13     }
    14 }());
    15 
    16 //控制逻辑
    17 ;(function(){
    18     //这里直接使用了新的api,因为移动应用可以这样任性。
    19     var pages = document.querySelectorAll('li');
    20     //左右翻页按钮
    21     var left = document.querySelector('.left');
    22     var right = document.querySelector('.right');
    23     //取得所有的子页面
    24     var pagesLen = pages.length-1;
    25     //屏宽
    26     var screenWidth = 320;
    27 
    28     //标记当前页面
    29     var currIndex = 0;
    30 
    31     //标记是否触发滑动
    32     var isTouch = false;
    33     //记录当前位置
    34     var axis = {
    35         x:0,
    36         y:0
    37     }
    38 
    39     //移动页面
    40     var move = function(index,width,time){
    41         var page = pages[index];
    42         page.style.transform = 'translate3d('+width+'px, 0, 0)';
    43         page.style.transitionDuration = time+'ms';
    44     }
    45 
    46     //上一页
    47     var toLeft = function(){
    48         if(currIndex >0){
    49             move(currIndex,-screenWidth,300);
    50             move(--currIndex,0,300);
    51         } 
    52     }
    53 
    54     //下一页
    55     var toRight = function(){
    56         if(currIndex < pagesLen){
    57             move(currIndex,screenWidth,300);
    58             move(++currIndex,0,300);
    59         }
    60     }
    61 
    62     //监听动作
    63     left.onclick = function(){
    64         toLeft();
    65     }
    66 
    67     right.onclick = function(){
    68         toRight();
    69     }
    70 
    71     document.addEventListener('mousedown',function(e){
    72         isTouch = true;
    73         axis.x = e.clientX;
    74         axis.y = e.clientY;
    75     });
    76 
    77     document.addEventListener('mousemove',function(e){
    78         if(isTouch){
    79             var distance = e.clientX - axis.x;
    80             if(distance>0){
    81                 //next
    82                 //此时需要看到实时移动效果,所以时间为0
    83                 move(currIndex,distance,0);
    84                 //当前页与上一页之间总是相差一个屏宽
    85                 move(currIndex+1,distance-screenWidth,0);
    86             }else{
    87                 //prev
    88                 move(currIndex,distance,0);
    89                 move(currIndex-1,screenWidth+distance,0);
    90 
    91             }
    92         }
    93     });
    94 
    95     document.addEventListener('mouseup',function(e){
    96         isTouch = false;
    97     });
    98 
    99 }())
    View Code

    这一步我们已经实现了拖动滑页效果,但是感觉怪怪的,对比一下手机上的滑动翻页效果发现,真机上只要我们滑动一定距离之后,页面就自动翻过去了,而不是要我们从一边一直滑到另一边,这样也太不实际了,而且如果我们只滑了一点距离,那么页面会自动归位,也就是常说的反弹效果。要实现这些也不难,我们继续完善代码。

      1 //初始化
      2 ;(function(){
      3     var pages = document.querySelectorAll('li');
      4     var width = 320;
      5     var len = pages.length;
      6     var setpost = function(element){
      7         element.style.transform = 'translate3d(-'+width+'px, 0, 0)';
      8     }
      9 
     10     //把除1以外的页堆在左边
     11     while(--len){
     12         setpost(pages[len]);
     13     }
     14 }());
     15 
     16 //控制逻辑
     17 ;(function(){
     18     //这里直接使用了新的api,因为移动应用可以这样任性。
     19     var pages = document.querySelectorAll('li');
     20     //左右翻页按钮
     21     var left = document.querySelector('.left');
     22     var right = document.querySelector('.right');
     23     //取得所有的子页面
     24     var pagesLen = pages.length-1;
     25     //屏宽
     26     var screenWidth = 320;
     27     //反弹时间
     28     var time = 300;
     29     //滑动距离
     30     var distance=0;
     31     //标记当前页面
     32     var currIndex = 0;
     33 
     34     //标记是否触发滑动
     35     var isTouch = false;
     36     //记录当前位置
     37     var axis = {
     38         x:0,
     39         y:0
     40     }
     41 
     42     //移动页面
     43     var move = function(index,width,time){
     44         var page = pages[index];
     45         page.style.transform = 'translate3d('+width+'px, 0, 0)';
     46         page.style.transitionDuration = time+'ms';
     47     }
     48 
     49     //上一页
     50     var toLeft = function(){
     51         if(currIndex >0){
     52             move(currIndex,-screenWidth,time);
     53             move(--currIndex,0,time);
     54         } 
     55     }
     56 
     57     //下一页
     58     var toRight = function(){
     59         if(currIndex < pagesLen){
     60             move(currIndex,screenWidth,time);
     61             move(++currIndex,0,time);
     62         }
     63     }
     64 
     65     //监听动作
     66     //prev
     67     left.onclick = function(){
     68         toLeft();
     69     }
     70     //next
     71     right.onclick = function(){
     72         toRight();
     73     }
     74 
     75     document.addEventListener('mousedown',function(e){
     76         isTouch = true;
     77         axis.x = e.clientX;
     78         axis.y = e.clientY;
     79     });
     80 
     81     document.addEventListener('mousemove',function(e){
     82         if(isTouch){
     83             distance = e.clientX - axis.x;
     84             if(distance>0){
     85                 //next
     86                 if(currIndex<pagesLen){
     87                     //此时需要看到实时移动效果,所以时间为0
     88                     move(currIndex,distance,0);
     89                     //当前页与上一页之间总是相差一个屏宽
     90                     move(currIndex+1,distance-screenWidth,0);
     91                 }
     92             }else{
     93                 if(currIndex>0){
     94                     //prev
     95                     move(currIndex,distance,0);
     96                     move(currIndex-1,screenWidth+distance,0);
     97                 }
     98             }
     99         }
    100     });
    101 
    102     document.addEventListener('mouseup',function(e){
    103         isTouch = false;
    104         //反弹条件
    105         var band = Math.ceil(screenWidth * 0.3);
    106         //next
    107         if(distance >0 && currIndex < pagesLen){
    108             if(distance > band){
    109                 toRight();
    110             }else{
    111                 //滑动距离太小,页面反弹
    112                 move(currIndex,0,time);
    113                 move(currIndex+1,-screenWidth,time);
    114             }
    115             return;
    116         }
    117         //prev
    118         if(distance < 0 && currIndex > 0){
    119             if(-distance > band){
    120                 toLeft();
    121             }else{
    122                 //反弹
    123                 move(currIndex,0,time);
    124                 move(currIndex-1,screenWidth,time);
    125             }
    126         }
    127     });
    128 
    129 }())
    View Code

     最后发现有一点小问题,滑动之后再点翻页按钮,乱套了。仔细分析之后,找出了问题所在,松手时的移动距离不应该用滑动时的最后距离,所以修复很容易。

      1 //初始化
      2 ;(function(){
      3     var pages = document.querySelectorAll('li');
      4     var width = 320;
      5     var len = pages.length;
      6     var setpost = function(element){
      7         element.style.transform = 'translate3d(-'+width+'px, 0, 0)';
      8     }
      9 
     10     //把除1以外的页堆在左边
     11     while(--len){
     12         setpost(pages[len]);
     13     }
     14 }());
     15 
     16 //控制逻辑
     17 ;(function(){
     18     //获取所有页面
     19     var pages = document.querySelectorAll('li');
     20     //左右翻页按钮
     21     var left = document.querySelector('.left');
     22     var right = document.querySelector('.right');
     23     //取得所有的子页面
     24     var pagesLen = pages.length-1;
     25     //屏宽
     26     var screenWidth = 320;
     27     //反弹时间
     28     var time = 300;
     29  
     30     //标记当前页面
     31     var currIndex = 0;
     32 
     33     //标记是否触发滑动
     34     var isTouch = false;
     35     //记录当前位置
     36     var axis = {
     37         x:0,
     38         y:0
     39     }
     40 
     41     //移动页面
     42     var move = function(index,width,time){
     43         var page = pages[index];
     44         page.style.transform = 'translate3d('+width+'px, 0, 0)';
     45         page.style.transitionDuration = time+'ms';
     46     }
     47 
     48     //上一页
     49     var toLeft = function(){
     50         if(currIndex >0){
     51             move(currIndex,-screenWidth,time);
     52             move(--currIndex,0,time);
     53         } 
     54     }
     55 
     56     //下一页
     57     var toRight = function(){
     58         if(currIndex < pagesLen){
     59             move(currIndex,screenWidth,time);
     60             move(++currIndex,0,time);
     61         }
     62     }
     63 
     64     //监听动作
     65     //prev
     66     left.onclick = function(){
     67         toLeft();
     68     }
     69     //next
     70     right.onclick = function(){
     71         toRight();
     72     }
     73 
     74     document.addEventListener('mousedown',function(e){
     75         isTouch = true;
     76         axis.x = e.clientX;
     77         axis.y = e.clientY;
     78     });
     79 
     80     document.addEventListener('mousemove',function(e){
     81         if(isTouch){
     82             //滑动距离
     83             var distance = e.clientX - axis.x;
     84             if(distance>0){
     85                 //next
     86                 if(currIndex<pagesLen){
     87                     //此时需要看到实时移动效果,所以时间为0
     88                     move(currIndex,distance,0);
     89                     //当前页与上一页之间总是相差一个屏宽
     90                     move(currIndex+1,distance-screenWidth,0);
     91                 }
     92             }else{
     93                 if(currIndex>0){
     94                     //prev
     95                     move(currIndex,distance,0);
     96                     move(currIndex-1,screenWidth+distance,0);
     97                 }
     98             }
     99         }
    100     });
    101 
    102     document.addEventListener('mouseup',function(e){
    103         //松手时的移动距离
    104         var distance = e.clientX - axis.x;
    105         //反弹条件
    106         var band = Math.ceil(screenWidth * 0.3);
    107         isTouch = false;
    108         //next
    109         if(distance >0 && currIndex < pagesLen){
    110             if(distance > band){
    111                 toRight();
    112             }else{
    113                 //滑动距离太小,页面反弹
    114                 move(currIndex,0,time);
    115                 move(currIndex+1,-screenWidth,time);
    116             }
    117             return;
    118         }
    119         //prev
    120         if(distance < 0 && currIndex > 0){
    121             if(-distance > band){
    122                 toLeft();
    123             }else{
    124                 //反弹
    125                 move(currIndex,0,time);
    126                 move(currIndex-1,screenWidth,time);
    127             }
    128         }
    129     });
    130 
    131 }())
    View Code

    到此,这个简易的移动应用就建好了。发现这其实只是搭好了一个架子,里边还有很多内容可以填。比如,给每页加点背景和文字,让页面看起来更加丰满,给标题栏加点工具图标,让它看起来更像是一个原生的apk程序。当我们双击或长按页面空白区域的时候,自动隐藏或显示翻页按钮,增加对更多浏览器的支持,增加对移动设备的支持........

    可以发挥的地方太多太多了,锦上添花的事就只好交给各位了。

    一步一步做下来,收获了些什么呢?总结一下:

    首先要有一个清晰的思路,先确定要实现什么功能,在脑海里有一个整的印象。把UI先建起来,这一步很容易实现,在此基础上,我们就可以看到,有哪些功能需要js去做,哪些效果需要css去做。先易后难。先整体后局部。在做的过程中,一步一步的完善,扩展,优化。一开始就想着要多完美,多优化,结果只是不断的否定自己的想法。不要怕出错,前面的演示中,我们发现,每一步都有些小错误,但是只要我们的整体思路是符合预期的,修正起来就会很快,这个修错的过程,也是一次学习的机地,总结的多了,以后就可以少出错,少被坑。

    效果图:

    加点内容,是不是立马显得高大上了呀!

    源码打包下载:https://github.com/bjtqti/swipe.git

    本文系原创,如果喜欢就砸个赞吧!

  • 相关阅读:
    C#跨窗体操作
    搞IT的不如去养鸡养猪了
    C# 委托实例(跨窗体操作控件)
    FastReport 自定义页长
    SQL 根据一个表更新另一个表的内容
    Delphi中用ADOQuery实现主从表的例子(转)
    旅行的意义
    嘉州影院的网址
    纯粹的人
    Delphi中流的使用:压缩与解压缩(TCompressionStream、TDecompressionStream)
  • 原文地址:https://www.cnblogs.com/afrog/p/4229718.html
Copyright © 2011-2022 走看看