H5頁面導航跟隨頁面滾動而聯動,及iPhoneX底部補白


 項目開發過程中遇到一個主頁,實現功能:

  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判斷)

//鈎子函數獲取頁面DOM
mounted(){ 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事件便會重新計算activeTab
changeTab(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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM