zoukankan      html  css  js  c++  java
  • 纯手工打造悬浮菜单

    有选择性的重复造一些轮子,未必是件坏事。Aaron的博客上加了一个悬浮菜单,貌似显得很高大上了。虽然这类小把戏也不是头一次见了,但是从未自己写过。今天就选择性的拿这个功能写一写。下面是这个轮子的开发过程,也可以当作是一篇需求文档的分析和实现过程。

    演示地址:http://sandbox.runjs.cn/show/to8wdmuy

    源码下载:https://github.com/bjtqti/study/tree/master/floatmenu

    第一步创建dom节构:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>AppCarrier</title>
        <link rel="stylesheet" href="menu.css">
    </head>
    <body>
    
        <div id="content">
                  <h1 id="test1">test1</h1>
                  <p>The past can hurt. But you can either run from it or learn from it</p>
                  <p>过去是痛楚的,但你要么逃避,要么从中成长</p>
                  <p>One meets his destiny on the road he takes to avoid it</p>
                  <p>往往在逃避命运的路上,却与之不期而遇</p>
                  <p>Rules are meant to be broken</p>
                  <p>规则就该被打破。</p>
                  <p>Years may wrinkle the skin, but to give up enthusiasm wrinkles the soul.</p>
                  <p>岁月流逝只令容颜苍老,激情不再却使心灵枯萎。</p>
                  <h1 id="test2">test2</h1>
                  <p>只有不断地练习学到的知识,你才能真正掌握它。</p>
                  <p>Live every day to the fullest.</p>
                  <p>尽享每日。</p>
                  <p>Keep your eyes on the stars, and your feet on the ground.</p>
                  <p>志存高远,脚踏实地。</p>
                  <p>Always be up for an unexpected adventure.</p>
                  <p>随时准备开始一场意外冒险吧。</p>
                  <p>Life is full of disappointment. You can't dwell on things. You have to move on.</p>
                  <p>生活常不如意,别沉溺往事,要勇往直前。</p>
                  <p>I'm a free spirit. I can't be caged.</p>
                  <p>我的灵魂是自由的,不该被束缚。</p>
                  <p>Sometimes the heart sees what is invisible to the eye.</p>
                  <p>目不见者,心可感之</p>
                  <p>The simple things are also the most extraordinary things, and only the wise can see them.</p>
                  <p>最平凡的事也是最非凡的事,只有智者才明白。</p>
                  <h1 id="test3">test3</h1>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <h1 id="test4">test4</h1>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
                  <p>how many xxxxxx</p>
        </div>
        
        <div class="menu" id="menubar">
            <p class="static">隐藏</p>
            <ul>
                <li><a href="#test1">test1</a></li>
                <li><a href="#test2">test2</a></li>
                <li><a href="#test3">test3</a></li>
                <li><a href="#test4">test4</a></li>
            </ul>
    
        </div>
     
    </body>
    <script src="menu.js"></script>
    </html>
    View Code

    第二步准备css文件:

    ul {
        list-style-type: none;
    }
    a {
        text-decoration: none;
    }
    /*文章内容区*/
    #content {
        width:400px;
        margin: 0 auto;
        font-size: 2em;
    }
    /*悬浮菜单*/
    .menu {
        position: fixed;
        top:20%;
        right: 0;
        width:200px;
        border: 1px solid gray;
        border-radius: 5px;
    }
    
    .menu li {
        height: 2em;
        line-height: 2em;
    }
    
    .red {
        color : red;
    }
    
    .hide {
        display: none;
    }
    
    /*隐藏悬浮菜单*/
    .slideIn {
        transform : translate3d(202px, 0, 0);
        transition-duration : .5s;
    }
    
    /*显示悬浮菜单*/
    .slideOut {
        transform : translate3d(0, 0, 0);
        transition-duration : .5s;
    }
    
    .static {
        color:#007aff;
        text-align: center;
    }
    
    /*显示悬浮球*/
    .toShow {
        display: block;
            width: 4.8em;
            height: 2em;
            line-height: 2em;
            border-radius: .5em;
        border:1px solid gray;
        transform : translate3d(-5em, 0, 0);
        transition-duration : 1s;
    }
    View Code

    第三步开始编写js代码:

    (function(doc){
        //收集各章节的链接位置
         var pos = [],
             //收集菜单上的项目
             links = doc.getElementsByTagName('a'),
             //收集章节的标题
             titles = doc.getElementsByTagName('h1'),
             //悬浮菜单
             menu = doc.getElementById('menubar'),
             //当前选择项
             currentItem=null;
    
         //添加红色样式
         var addClass = function (element){
                 currentItem && currentItem.removeAttribute('class');
                 element.setAttribute('class','red');
                 currentItem = element;
             },
    
             //网页被卷去的高:
            getScrollTop = function (){
                return Math.ceil(document.body.scrollTop)+1;
            },
    
             //计算滚动位置
            startScroll = function (){
                var scrollTop = getScrollTop(),
                    len = titles.length,
                    i = 0;
    
                //第一条
                if(scrollTop>=0 && scrollTop<pos[0]){
                    addClass(links[0]);
                    return;
                }
                //最后一条
                if(scrollTop>=pos[len-1]){
                    addClass(links[len-1]);
                    return;
                }
                //中间
                for(;i<len;i++){
                    if(scrollTop > pos[i] && scrollTop < pos[i+1]){
                        addClass(links[i]);
                        break;
                    }
                }
        };
    
         //点击列表中的链接变色
         menu.onclick=function(e){
             var target = e.target || e.srcElement;
              
             if(target.nodeName.toLowerCase() === 'a'){
                 //列表项状态指示
                 addClass(target);
                 return;
             }
    
             if(target.nodeName.toLowerCase() === 'p'){
                 if(target.className == 'static'){
                     //隐藏菜单
                     this.className = 'menu slideIn';
                     setTimeout(function(){
                         target.className = 'static toShow';
                         target.innerHTML = '显示';
                     },1000);
                 }else{
                     //显示菜单
                     target.className = 'static';
                     target.innerHTML = '隐藏';
                     this.className = 'menu slideOut';
                 }
             }
         }
    
        //计算各章节的初始位置
        var ln = titles.length;
        while(--ln>-1){
            //titles[len].offsetParent.offsetTop = 0;
            pos.unshift(titles[ln].offsetTop);
        }
    
        startScroll();
    
        //监听滚动
        window.onscroll = function(){
              startScroll()
        }
    
    })(document);
    View Code

    分析:

        1. 实现自动跳转到指定节

              这一步可以利用<a>标签的锚功能来做,由于html5以后不支持name 属性(HTML5 不支持。规定锚的名称。),所以考虑用ID来跳转。

        2. 标识悬浮菜单中的项属于左边内容中的哪个章节。

              这一步是难点,先简单分析一下:

                2.1 第一种情况,就是人为点击菜单项。这个很容易,只要标识点击的元素就可以了。

                2.2 第二种情况,通过鼠标中键滚动或拖动滚动条,这个时候要关联左边内容和右边菜单项,这是最难的地方。考虑分步实施,先易后难,各各击破的策略。

                 2.2.1 先收集标题元素的坐标高度。也就是所有的h1标签的垂直高度。存入数组1.

                 2.2.2 收集菜单项中的a元素,存入数组2.

                    2.2.3  监听滚动事件,判断当前内容属于哪个菜单项。

                        做一步的时候,建议在稿纸上画一个图:

                A1

                             ****************
                             *       A2
                             *
                             ****************
                             *       A3
                             *
                             ****************
                             *
                             *     A4
                             *

           每滚动一次,就判断当前滚动的距离是在哪一个区间,如果是0到A1则是第1章,A1到A2则是第2章,以此类推。

                        关于元素的位置高度,我这里简单地用element.offsetTop来获取,可能会存在兼容性问题,如果用jquery来做的话,应当是$('element').offset().top,

                        同样的,滚动条的高度,我也是简单的用了document.body.scrollTop来获取,如果换成jquery的话,应当是$(window).scrollTop();

                       画图的作用是把抽象的问题具体化,帮助我们思考,找出规律。也许称为“建模”会显得高大上一些吧。

                        需要强调的是数组1和数组2中的关系应当是一一对应的。如<a href="#h1">对应的是<h1 id="h1">。

               2.3 第三种情况,直接进入页面时的菜单状态指示。比如通过index.html#h3这样的地址进来,菜单中的h3应当要突出显示。

        3.  实现悬浮菜单的显示和隐藏动画。

            3.1  这一步应当是比较简单的,可以考虑先做。利用css3的tramsform属性就可以了,简单高效,跨浏览器的话,注意兼容。

          注意transform : translate3d(x轴, y轴, z轴); 用3d是可以利用硬件加速,增加动画效果,但是功耗会增加,善用!第一个参数是控制左右方向,如果为正,则表示向右移动,如果为负则向左移动。这么说其实是不严谨的,实际上应当根据参考点来确定,比如元素的静止时的x坐标是0,那么增加x的值向右,减少为向左,0为复位。

        分析完之后,就是编写代码了。这没有什么好说的。尽情享受敲击键盘产生的乐感吧。

        写完之后,预览一下,点击菜单,跳入指定章节,同时点击项变红色,刷新当前页面,依赖显示正确。滑动一下滚轮,菜单项随着内容的变化而相应的变化,拖动一下滚动条,也是这样,最后点击一下隐藏,菜单缩回去,点击显示,菜单滑出来。这样悬浮功能就做完了。

     查看源代码:http://runjs.cn/code/to8wdmuy

  • 相关阅读:
    2018左其盛差评榜(截至10月31日)
    4星|《环球科学》2018年10月号:习惯悬崖上生活的游隼现在开心地在摩天大楼上捕食鸽子
    4星|《财经》2018年第25期:中国的五大主要城市群具备“大、快、活”三个独特优势
    2018左其盛好书榜(截至10月31日)
    4星|《门口的野蛮人1》:1988年惊动美国的一次杠杆收购事件
    在 ASP.NET Core 具体使用文档
    .Net Core 部署到 CentOS7 64 位系统中的步骤
    [Linux/Ubuntu] vi/vim 使用方法讲解
    CentOS7使用httpd apache 和firewalld打开关闭防火墙与端口
    Linux软件管理器(如何使用软件管理器来管理软件)2---安装及管理Linux应用程序
  • 原文地址:https://www.cnblogs.com/afrog/p/4286538.html
Copyright © 2011-2022 走看看