微信小程序底部内容被底部tabbar遮挡

4,775 阅读2分钟
  • 在微信小程序社区看到有遇到同类情况的问题: developers.weixin.qq.com/community/d… developers.weixin.qq.com/community/d…

  • 问题:小程序中在有底部tabbar时,无法直接获取底部tabbar高度,导致可滚动区域的高度值计算不准确,在scrollview上下滚动时,容易触发外层page滚动条显示,出现双滚动条。尤其当scrollview滚动至底部时不能完全展示完整内容,需再次将外层page滚动条滑动至底部才能看到完整内容,即tabbar遮挡底部内容。影响功能、用户体验不好、也不美观。

  • 问题预览:

  • 方案:

在页面底部增加view#bottomWatch,设置绝对定位,作为可滚动区域底部边界。(也可通过它和屏幕高度计算获取到实际底部tabbar高度)

蓝块:#bottomWatch滚动区下边界距离顶部距离,即不含底部tabbar的屏幕高度

红块:滚动区上边界距离顶部距离

黄块:在顶部导航吸顶后需隐藏的动态内容区域高度(如不需要,可忽略)

预览: 完整代码:

页面中使用方式: 1.index.wxml:

<view id="bottomWatch" class="bottom-watch">
</view>

index.wxss:

.bottom-watch {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: transparent;
  visibility: hidden;
}

index.js:

import setRealHeight from "../../api/setRealHeight"

Page({
  onload() {
    setRealHeight.bind(this, {
      topBoundaryClass: ".tab-bar",//顶部分类导航选择器
      dynamicHeightClass: ".adCard",//顶导上的其他动态隐藏和显示内容区选择器
    })()
    //或者
    setRealHeight.bind(this, {
      topBoundaryClass: ".tab-bar",//顶部分类导航选择器
    })()
  }
})

setRealHeight.js:

/**
 *
 * @method handleSelectorQuery 内部使用
 * @params {
  * className: "", 监听的元素选择器
  * rectElement: "", 所需rect中的具体元素,rect值参考:https://developers.weixin.qq.com/miniprogram/dev/api/wxml/NodesRef.boundingClientRect.html
  * heightObjElement: "", 作为接收结果属性
  * heightObj: {}, 接收结果,用于setdata前的结果计算
 * }
 *
 * @method setRealHeight 对外接口
 * @params {
  * topBoundaryClass: "", 所需结果高度的上边界的选择器名,用于计算距离元素#bottomWatch之间的高度
  * dynamicHeightClass: "", 高度动态变化元素的选择器名,如吸顶效果中发生变化的元素
 * }
 */

function handleSelectorQuery(params = {
  className: "",
  rectElement: "",
  heightObjElement: "",
  heightObj: {},
}) {
  const {
    className, rectElement, heightObjElement, heightObj,
  } = { ...params }
  return new Promise((resolve, reject) => {
    wx.createSelectorQuery().select(`${className}`).boundingClientRect((rect) => {
      if (rect && rect[`${rectElement}`]) {
        heightObj[`${heightObjElement}`] = rect[`${rectElement}`]
        resolve()
      } else {
        reject()
      }
    }).exec()
  })
}

export default function setRealHeight(params = {
  topBoundaryClass: "",
  dynamicHeightClass: "",
}) {
  const heightObj = {}
  const topBoundaryHeightPromise = params.topBoundaryClass && handleSelectorQuery({
    className: params.topBoundaryClass,
    rectElement: "bottom",
    heightObjElement: "topBoundaryHeight",
    heightObj,
  })
  const dynamicHeightPromise = params.dynamicHeightClass && handleSelectorQuery({
    className: params.dynamicHeightClass,
    rectElement: "height",
    heightObjElement: "dynamicHeight",
    heightObj,
  })
  Promise.all([topBoundaryHeightPromise, dynamicHeightPromise]).then(() => {
    const query = wx.createSelectorQuery()
    query.select("#bottomWatch").boundingClientRect()
    query.selectViewport().scrollOffset()
    query.exec((res) => {
      if (res && res[0]) {
        const tempData = res[0].bottom - heightObj.topBoundaryHeight
        const resultData = {
          realWinHeight: tempData,
        }
        if (params.dynamicHeightClass) {
          resultData.realWinHeightOfFixTab = tempData + heightObj.dynamicHeight
        }
        this.setData(resultData)
      }
    })
  })
}