uni-app 小程序中锚点的使用

2,013 阅读3分钟

最近在负责公司微信挂号小程序重构,第一次搞小程序,碰到很多坑,其中有一个功能是这样的,点击tab不同项跳转到页面不同地方(看图就行)。一开始的想法是,进入页面的时候,使用 boundingClientRect() 动态获取那3个元素的高度,点击后再使用scroll-view的scroll-top属性设置滚动条的位置。但是这样做有点缺陷,因为boundingClientRect获取的是元素离页面顶部的距离。它返回的每个点的坐标会随着屏幕滑动而变化。后面想到了锚点,小程序可以通过使用scroll-view的scroll-into-view属性来实现锚点功能。使用scroll-y属性时,必须设置高度。

1629897702353.gif

    <div id="imageSlide" @click.stop="scrollIntoView('imageSlide')">商品</div>
    <div id="goodsDetail" @click.stop="scrollIntoView('goodsDetail')">详情</div>
    <div id="orderDesc" @click.stop="scrollIntoView('orderDesc')">须知</div>
		
    <scroll-view 
       @scroll="scroll" 
       scroll-x="false" 
       :scroll-top="scrollTop" 
       :style="{height: winHeight}"  
       :scroll-into-view="toView"
       :scroll-y="canScroll" 
     >
        <div id="imageSlide">商品</div>   <!-- 需要跳转到的地方,要设置id -->
        <div id="goodsDetail">详情</div>
        <div id="orderDesc">须知</div>
     </scroll-view>
     
     data() {
         toView: '', // 锚点名称,点击不同tab
         winHeight: '100%'
     },
     onShow() {
         uni.getSystemInfo({
             success: (res) => { 
                console.log(res.windowHeight); 
                
                this.winHeight = res.windowHeight*2 - 100 + 'rpx'  // 100rpx为导航栏高度
             }
             
             
         })
     }
     

     
     scrollIntoView(name) {
        this.toView = name
        if (this.timeout) {
            clearTimeout(this.timeout)
        }
        this.timeout = setTimeout(() => {
            this.toView = ''
        })
     }

为了防止滚动页面在点击之前的锚点跳转无效,可以监听滚动事件,滚动时将scroll-into-view属性的值清空,或者在每次锚点跳转后,再由一个异步操作将scroll-into-view属性的值清空。

一些小问题以及优化

  • 因为tab使用的时固定定位,所以在锚点跳转的时候,会遮住一部分内容,导致跳转后看起来不准确,这个时候可以添加一个空的div(和tab高度一致)进行占位,然后跳转到div上就行,通过是否点击了tab来控制div是否展示即可。在微信开发者工具跟安卓手机上面没有问题,但是在ios系统上,依旧会遮住标题。后面想到进入页面的时候,获取手机屏幕高度,动态设置scroll-view高度。

  • 因为还要实现一个页面滑动,tab自行切换的效果。这个时候要使用boundingClientRect来获取元素的距离,但是有一个缺陷是,如果用户在接口请求并完成页面渲染之前滑动了页面,会导致boundingClientRect获得的距离不是我们想要的。我想到的是添加一个加载中的提示来提示用户加载完成前不要滑动页面或者动态设置scroll-y,如果没有渲染完成就设置为false来禁止用户滑动。

     const query = uni.createSelectorQuery().in(this);  
     query.select('#id').boundingClientRect(data => { 
         console.log("得到布局位置信息" + JSON.stringify(data)); 
         console.log("节点离页面顶部的距离为" + data.top); 
     }).exec();
    

1629900982307.gif

  • 还有一个就是我的一些接口请求都写在onShow这个生命周期里面(boundingClientRect也是),这样有如果用户滑动了页面后,再进行操作进入下一个页面,然后返回上一页,再次进入这个页面的时候。页面滑动,tab自行切换以及锚点都有问题。我们可以把boundingClientRect写在mounted里面

    • onShow监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面
    • mounted挂载到实例上去之后调用,只调用一次