轮播,这个概念只要做过 UI 的都不会陌生,盲猜市场上 90% 的应用都有这个需求,在 iOS 和 Android 上都有很完善的控件,比如 Android 的 ViewPager 和 iOS 的 UIScrollview。
小程序这么牛逼,肯定也要有控件支持这个特性啊, swiper
就这么诞生了。
但是 swiper
有一个很严重的问题,就是高度默认 150px,且不可以自适应内容调整高度。
这就有问题了,我现在有一个多 Tab 的页面,最少高度要满屏,还要超出内容可以往下滚动,此时就蒙蔽了,怎么给 swiper
设置高度呢?
首先看一下我搜索到的一些方法:
-
在初始化的时候获取到屏幕的高度,然后将高度设置到
swiper
上,至于滚动的问题,在里面再嵌入一个scroll-view
这个问题有很多坑,首先 屏幕的高度要比内容区的高度大,这么设置以后就算内容较少,页面也能滑动一点;其次,小程序的
scroll-view
在实现上拉加载更多的时候,坑更多。 -
每个 item 的高度都一致,根据 item 的数量和统一的高度计算出内容的高度,然后设置进去
这个方案感觉完全是 zz 方案,局限性太大了
我的方案
一句话解释:给 swiper-item 内部添加三个锚点,最上面一个,最下面一个,还有一个锚点始终位于屏幕最底下。根据这三个锚点计算出内容高度和内容显示区高度。
PS:锚点,宽高为 0 的不可见的 view,用于获取定位
如果还有不理解可以看下面这个示意图:
这三个锚点的具体作用是用来计算 swiper 内容高度和 swiper 距离屏幕底部的具体,计算方式如下:
- 使用
swiper-item
内部的两个锚点计算出内容区高度 - 通过屏幕底部和
swiper-item
顶部的锚点计算出离屏幕底部的距离
接下来看看代码具体实现
代码实现
page.wxml
<view>
<swiper style="height: {{anchor.deviceHeight + 'px'}}">
<swiper-item>
<view class="anchor-top"></view>
<!-- 你的内容 -->
<view class="anchor-bottom"></view>
</swiper-item>
</swiper>
<view class="anchor-screen-bottom"></view>
</view>
page.wxss
.anchor-top {
width: 0;
height: 0;
}
.anchor-bottom {
width: 0;
height: 0;
}
.anchor-screen-bottom {
position: absolute;
bottom: 0;
width: 0;
height: 0;
}
page.js
Page({
data: {
anchor: {
deviceHeight: 0,
anchorTop: 0,
anchorBottom: 0,
anchorScreenBottom: 0
}
},
onReady: function() {
this.computeSwiperHeight(0)
},
computeSwiperHeight(pageIndex) {
let getSwiperHeight = () => {
let min = this.data.anchor.anchorScreenBottom - this.data.anchor.anchorTop;
let value = this.data.anchor.anchorBottom - this.data.anchor.anchorTop
return Math.max(min, value)
}
wx.createSelectorQuery()
.select('.anchor-screen-bottom')
.boundingClientRect()
.selectViewport()
.scrollOffset()
.exec(res => {
this.data.anchor.anchorScreenBottom = res[0].bottom
})
wx.createSelectorQuery()
.selectAll('.anchor-top')
.boundingClientRect()
.selectViewport()
.scrollOffset()
.exec(res => {
this.data.anchor.anchorTop = res[0].top
this.setData({
'anchor.deviceHeight': getSwiperHeight()
})
})
wx.createSelectorQuery()
.selectAll('.anchor-bottom')
.boundingClientRect()
.selectViewport()
.scrollOffset()
.exec(res => {
this.data.anchor.anchorBottom = res[0].bottom
this.setData({
'anchor.deviceHeight': getSwiperHeight()
})
})
},
})
适配多页面
当然,肯定要适配每个页面的高度不一样的情况。方案也很简单,屏幕底部的锚只需要一个了,给每个 swiper-item 的都添加两个锚点,和之前一样一个在上面一个在下面,在切换页面的时候,根据当前页面的锚点重新计算一下高度,然后设置进去。
只需要在原有基础上改一下代码:
page.wxml
<view>
<swiper style="height: {{anchor.deviceHeight + 'px'}}" bindchange="swiperChange">
<swiper-item>
<view class="anchor-top"></view>
<!-- 你的内容 -->
<view class="anchor-bottom"></view>
</swiper-item>
<swiper-item>
<view class="anchor-top"></view>
<!-- 你的内容 -->
<view class="anchor-bottom"></view>
</swiper-item>
</swiper>
<view class="anchor-screen-bottom"></view>
</view>
page.wxss
CSS 不需要改动
page.js
Page({
data: {
anchor: {
deviceHeight: 0,
anchorTop: 0,
anchorBottom: 0,
anchorScreenBottom: 0
}
},
onReady: function() {
this.computeSwiperHeight(0)
},
swiperChange(e) {
this.computeSwiperHeight(e.detail.current)
},
computeSwiperHeight(pageIndex) {
let getSwiperHeight = () => {
let min = this.data.anchor.anchorScreenBottom - this.data.anchor.anchorTop;
let value = this.data.anchor.anchorBottom - this.data.anchor.anchorTop
return Math.max(min, value)
}
wx.createSelectorQuery()
.select('.anchor-screen-bottom')
.boundingClientRect()
.selectViewport()
.scrollOffset()
.exec(res => {
this.data.anchor.anchorScreenBottom = res[0].bottom
})
wx.createSelectorQuery()
.selectAll('.anchor-top')
.boundingClientRect()
.selectViewport()
.scrollOffset()
.exec(res => {
this.data.anchor.anchorTop = res[0][pageIndex].top
this.setData({
'anchor.deviceHeight': getSwiperHeight()
})
})
wx.createSelectorQuery()
.selectAll('.anchor-bottom')
.boundingClientRect()
.selectViewport()
.scrollOffset()
.exec(res => {
this.data.anchor.anchorBottom = res[0][pageIndex].bottom
this.setData({
'anchor.deviceHeight': getSwiperHeight()
})
})
},
})
实现效果
-
swiper
的高度高度根据内容自适应 -
swiper
的高度最小占满屏幕,最大和内容一样高(为了用户滑动体验(如果划页后高度突然变小,用户在原来的位置就划不回去了) - 适配不同高度的页面
这个方案是了实现为自己的需求而写的,应该不适应全部的场景,不过希望可以为你提供一点思路。
感想
小程序里的坑真的很多,而且有些 API 设计的很奇怪,真的不知道当初开发人员怀着怎样的心路历程设计出的 API。
个人感觉小程序就是给前端新造了一个轮子,更新还很不及时,有很多陈年老 Bug,比如本文讲的 swiper
。
前端娱乐圈发展这么快,感觉小程序可能会跟不上潮流。
最后
给我正在开发的小程序预热一下~
欢迎关注~