H5页面导航跟随页面滚动而联动

项目开发过程中遇到一个主页,实现功能:

  1. 需要页面在滚动到导航时导航吸顶;
  2. 导航随页面滚动高亮选中;点击导航页面滚动到固定位置;
  3. 在导航项过多时导航横向滚动;
  4. 最后一个面板底部补白且兼容iphoneX。

页面区域划分:

  1. 头部数字显示(在页面上拉后隐藏)
  2. 导航部分
  3. 面板部分(所有的面板放在一个大div里)

效果如下图:

         

一:页面在滚动到导航时导航吸顶(sticky粘性布局)

<!--头部数字--><div class="head-number"></div><!--导航-->
<div id="Tab" class="tab">
       <span v-for="(v,i) in tabOption"  :class="{‘cur‘:i ===activeTab}" @click="changeTab(i)">{{v.v}}</span>
</div><!--面板区域-->
<div class="main-box">
        <div class="pannel" v-for="(v,i) in tabOption"></div>
</div>
export default {
   data() {
       return {
           activeTab: 0,//当前活动的tab项
           tabOption: [{k:‘netValue‘,v:‘净值‘},{k:‘notice‘,v:‘公告‘},{k:‘product‘,v:‘概况‘},{k:‘manager‘,v:‘投资经理‘},                          {k:‘heavyStock‘,v:‘隐形重仓股‘}{k:‘relatedSecurities‘,v:‘管理人管理的产品‘}}}},
<style scoped lang="less">    .tab{         position:sticky;         position:-webkit-sticky;         top:0;         z-index:1;         white-space:nowrap;         overflow-x:scroll;         -webkit-overflow-scrolling:touch;     }</style>

 sticky使用需要注意:1.父元素不能设置overflow:auto/hidden/scroll;

                             2.必须指定top、left、right、bottom其中一个,否则只会处于相对定位;

                            3.父元素的高度不能低于sticky元素的高度;

                            4.sticky仅在其父元素内有效;

二:导航随页面滚动高亮选中;点击导航页面滚动到固定位置(通过scrollTop和offsetTop判断)

//钩子函数获取页面DOMmounted(){
    const pannel = document.querySelectorAll(‘.pannel‘);
    window.addEventListener(‘scroll‘, throttle(() => {
           let head_num = document.querySelector(‘.head-number‘),
               head_num_height = parseFloat(getComputedStyle(head_num)[‘margin-bottom‘]) + head_num.clientHeight;
           let top = document.body.scrollTop || document.documentElement.scrollTop;
                    for (let i = this.tabOption.length; i > 0; i--) {
                        //倒叙缩减循环次数
                        //js会对小数部分自动四舍五入,导致计算出现偏差,故+1解决
                        if (top + 1 >= pannel[i - 1].offsetTop + head_num_height) {
                            this.activeTab = i-1;
                            break;
                        }
                    }
    }, 0));
}//使用activeTab绑定class来控制导航高亮;点击导航只要改变body的scrollTop即可触发window.scroll事件便会重新计算activeTabchangeTab(i){     const head_num = document.querySelector(‘.head-number‘),            head_num_height = parseFloat(getComputedStyle(head_num)[‘margin-bottom‘]) + head_num.clientHeight,            s_top = document.querySelectorALL(‘.pannel‘)[i].offsetTop + head_num_height;     document.body.scrollTop = document.documentElement.scrollTop = s_top;}

 计算原理见上图二中标注,注:div.offsetTop是div的上边款与带有定位元素(absolute、relative、fixed)的父元素(如果父元素不是定位元素,则继续上溯所有祖先直到body)的上边框之间的距离(只读属性)

三、在导航项过多时导航横向滚动

Tab导航由最外层padding(绿色)与span的margin(橘色)组成;

offsetWidth为width + padding + border(span的各自蓝色部分,tab的整个图部分);

offsetLeft为当前元素边框到定位父元素边框的距离(红线部分)

 //监听activeTab来改变tab的scrollLeft值
 watch: {
            activeTab(v){
                let $tab = $(‘#Tab‘),
                    tabWidth = $tab[0].offsetWidth,
                    tabPaddingLeft = parseFloat(getComputedStyle($tab[0])[‘paddingLeft‘]),
                    scrollLeft = $tab[0].scrollLeft;

                let node = $tab.find(‘span‘)[v],
                    nodeOffsetLeft = node.offsetLeft,
                    marginLeft = parseFloat(getComputedStyle(node)[‘marginLeft‘]),
                    spanPlaceWidth = node.offsetWidth + marginLeft;

                if(scrollLeft > nodeOffsetLeft) {//从左往右移动
                    $tab.scrollLeft(nodeOffsetLeft - marginLeft - tabPaddingLeft);
                }else if(scrollLeft + tabWidth < nodeOffsetLeft + spanPlaceWidth) {//从右往左移动
                    $tab.scrollLeft(nodeOffsetLeft - tabWidth + spanPlaceWidth + tabPaddingLeft);
                }
            },
        },

 四、最后一个面板底部补白且兼容iphoneX

为了使最后一个面板能够被拉起来,那它的高度需要等于页面高度减去导航高度(此时只有导航没有头部数字部分),考虑到iphoneX底部小黑条的原因,故再需要加上34px

//自动撑开底部
autoFillFoot(){
    const headHeight = document.getElementById(‘Tab‘).offsetHeight;//头部固定高度
    let pannel = document.getElementsByClassName(‘pannel‘);//滚动块
    const windowHeight = common.getWindowHeight(),
        lastPanelHeight = pannel[pannel.length - 1].clientHeight;
    let needPatchBottom = false;
    if (navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) { //iPhone;iPad;iPad Pro;
        if ((screen.height === 812 && screen.width === 375 && window.devicePixelRatio === 3) || //X;XS;
            ((screen.height === 896 && screen.width === 414) && (window.devicePixelRatio === 2 || window.devicePixelRatio === 3 ))){//2:XR;3:XS Max
            needPatchBottom = true;
        }
    }
    //safe-area-inset-bottom:34px;safe-area-inset-top:88px;
    if (lastPanelHeight < windowHeight) {
        const paddingBottom = parseFloat(getComputedStyle(pannel[pannel.length-1])[‘padding-bottom‘]);
        pannel[pannel.length - 1].style.height = `${windowHeight - headHeight  - paddingBottom + (needPatchBottom ? 34 : 0)}px`;
    }
},

同样页面样式也需要兼容iphoneX

@media only screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3),/* X,XS */
    only screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2),/* XR */
    only screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) {/* XS Max */
        :root {/* +-px是为了防止编译把100%变为1 */
            height: calc(100% - 1px + constant(safe-area-inset-bottom) + 1px);/* 兼容 iOS < 11.2 */
            height: calc(100% - 1px + env(safe-area-inset-bottom) + 1px);/* 兼容 iOS >= 11.2 */
        }
    }

关于页面兼容iphone刘海、小黑条等可参考

https://www.cnblogs.com/lolDragon/p/7795174.html

https://objcer.com/2017/09/21/Understanding-the-WebView-Viewport-in-iOS-11-iPhone-X/

原文地址:https://www.cnblogs.com/caofeng11/p/11157107.html

时间: 2024-10-10 00:10:24

H5页面导航跟随页面滚动而联动的相关文章

WinPhone学习笔记(一)——页面导航与页面相关

最近学一下Windows Phone(接下来简称“WinPhone”)的开发,在很久很久前稍探究一下WinPhone中对一些传感器的开发,那么现在就从头来学学WinPhone的开发.先从WinPhone的页面入手,在我印象中比较深刻的那番话:一台WinPhone设备就好比一个Web的浏览器,应用上每个界面就是一个网页,可以点击“后退”来返回之前的页面.这个类比我觉得相当的形象.这番话能引出WinPhone开发中一个比较常见的操作——页面导航,由这个页面导航还引出了别的方面的内容,如下面所示 那下

构建基于WinRT的WP8.1 App 01:页面导航及页面缓存模式

本篇博文主要阐述基于Windows Runtime的Windows Phone 应用页面间导航相关知识,主要分为以下几个方面: Window.Frame和Page概览 页面间实现跳转 处理物理后退键 页面的缓存 Window.Frame和Page概览 基于WinRT的Windows Phone 8.1,每个App只有一个Window. 每个Window都有自己的Frame和导航栈, 以及自己的Page. Window中有一个Frame,并且100%撑满可视区域,通常Frame也是100%撑满Wi

wp8.1 Study1: 页面导航&amp;页面间值传递

摘要:wp8.1与wp8中很多API是不一样了,wp8.1把以前wp7.x时的api去掉了,更多与win8.1的API相似.比如以下的页面导航和页面之间的值传递 1.页面导航 利用Frame.Navigate() 方法,C#语句如下: 1 Frame.Navigate(typeof(Page2));//Page2为一个页面的名称 这里需要注意的是此方法是可以重载的,即后面介绍的页面间值传递方法. 2.页面间值传递 这里利用Frame.Navigate()重载方法,进行单个值传递和多个值传递 1)

h5之scrollIntoView控制页面元素滚动

如果滚动页面也是DOM没有解决的一个问题.为了解决这个问题,浏览器实现了一下方法,以方便开发人员如何更好的控制页面的滚动.在各种专有方法中,HTML5选择了scrollIntoView()作为标准方法.scrollIntoView()可以在所有的HTML元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视窗中.如果给该方法传入true作为参数,或者不传入任何参数,那么窗口滚动之后会让调动元素顶部和视窗顶部尽可能齐平.如果传入false作为参数,调用元素会尽可能全部出现在视口中(可

js实现浮动框跟随页面滚动,最后停留在原来位置

左边悬浮的二维码会跟随页面向上或者向下滚动,最后停留在原来的位置. <div style="background:red; width:1000px; height:7000px; margin:auto;">  </div> <div id="aa" style="position: absolute;right: 0px; top: 4600px;"><img src="001.jpg&qu

jQuery 顶部导航跟随滚动,固定浮动在顶部

jQuery 顶部导航跟随滚动,固定浮动在顶部 演示 XML/HTML Code <section> <article class="left"> <p> </p> <ul> <li><a href="http://freejs.net/article_jquerywenzi_149.html" title="Ajax 动态加载内容">Ajax 动态加载内容<

[Aaronyang] 写给自己的WPF4.5 笔记17[Page实现页面导航]

1. 第一个Page页使用 新建PageDemo解决方案,默认wpf应用程序 右键项目新建页,然后指定App.xaml的默认启动窗口,为Page1.xaml,F5运行项目 2.关于NavigationWindow不推荐使用,所以不讲解了,创建对象,指定Content为一个Page页,然后Show() 3.Page类 讲解几个可能跟Window不一样的属性 ShowsNavigationUI="False" 不显示导航栏,默认显示 新建Window1.xaml窗口,放入Frame,去掉默

【REACT NATIVE 系列教程之五】NAVIGATOR(页面导航)的基本使用与传参

本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/react-native/2248.html 今天介绍一种应用开发中常用的负责页面切换及导航功能的组件:Navigator 一:Navigator 对于页面导航其实主要功能就是:每个页面都知道本身应该切换到哪个页面,并且切到的页面会记录从哪里来,如果要返回的话,知道返回到哪个页面.这一切都不需要再用逻辑管理!而且每个页面之间也可以进行参数传递,

【Win10】页面导航的实现

注:本文基于 Windows 10 10240 及其 SDK 编写,若以后有变化,请以新版本为准. 页面导航我们是再熟悉不过了,浏览器.手机 App 大多都使用这种方式来展示内容.在 Windows 10 应用商店应用当中,也是使用这种方式来展示内容.具体是通过 Frame 这个控件来进行导航展示. 在 App.xaml.cs 文件中,我们可以看到创建了一个 Frame: 并且在下面,使用 Navigate 方法导航到 App 的主页 MainPage. 导航到某个页面使用的就是 Navigat