本周一直在做 uniapp 的微信小程序项目,因此涉及的问题都是在该场景下出现
scroll-view 高度计算
在涉及下拉刷新和上拉加载的场景,scroll-view 是不错的选择,使用的时候需要传一个固定高度,如何准确计算出滚动区域高度,就很重要

如图,底部粉色区域为滚动区域,要计算滚动区域高度,用页面高度(100vh)减去其他区域(红色,绿色,蓝色)高度即可,还需要注意在滚动区域以外,如果有设置 marginTop,marginBottom,也要减去这个高度
在设计页面的时候,最好区分好每个部分的功能,不仅开发起来方便,也有利于计算高度,最后得出滚动区域高度
javascript
this.scrollViewHeight = `calc(100vh - (${statusBarHeight}px + ${navigationBarHeight}px + ${uni.upx2px(290)}px + ${uni.upx2px(44 + 10)}px))`;
红框部分实现的是自定义导航栏:高度为${statusBarHeight}px + ${navigationBarHeight}px
蓝框部分用rpx
设置的相对高度,需要用uni.upx2px(290)
蓝框部分设置的44rpx
加上蓝框和红框之间margin
,同样要用uni.upx2px(44 + 10)
转化
这样就能准确得出滚动区域高度,但如果其他区域没有设置高度,如何获取该区域高度呢,请接着看自定义 tabbar
自定义 tabbar
还是上面的页面,我需要实现一个 tabbar,要求当前激活项下显示红色短线,并且切换项目时候,短线有移动动画
先根据需求设置基本样式,.tab-item
为选项卡部分,.active-bar
为底部红色短线
vue
<view class="tabs">
<view v-for="(item, idx) in tabList" :key="item.name" class="tab-item"
:class="{ active: idx === activeTabIndex }" @click="changeTab(idx)">
{{ item.name }}
</view>
<view class="active-bar" :style="{
left: activeBarLeftPx + 'px'
}">
</view>
</view>
部分样式
css
.active-bar {
position: absolute;
bottom: -6rpx;
height: 6rpx;
border-radius: 30rpx;
background-color: rgba(255, 43, 88, 1);
width: 40rpx;
}
难点在于获取当前选项卡的位置,才能正确设置短线的位置,因为在 uni 的小程序平台不支持页面的 ref 属性,这里要用到uni.createSelectorQuery
api
在页面加载完成后,查询默认第一个选项卡位置
vue
mounted() {
// 初始化时获取第一个tab的位置
this.$nextTick(() => {
this.queryTabRect(this.activeTabIndex);
});
},
methods: {
queryTabRect(idx) {
uni.createSelectorQuery()
.in(this)
.selectAll('.tab-item')
.boundingClientRect(rects => {
if (rects && rects[idx]) {
this.activeBarLeftPx = rects[idx].left + (rects[idx].width - uni.upx2px(40)) / 2; // 相对导航栏左侧
}
})
.exec();
},
}
this.activeBarLeftPx
是.active-bar
相对父容器的距离,如何计算距离呢,rects[idx].left
是当前选项卡距离父容器左边距,(rects[idx].width - uni.upx2px(40)) / 2
是选项卡和底部短线在竖直方向居中对齐时,多出来长度的一半,加上这个长度,就能正确计算出底部短线距离父容器左边框的距离
容器高度
回到上面scroll-view 高度计算
部分,其他区域高度也可以使用uni.createSelectorQuery
api 获取,结果就是rects[idx].height
,需要注意这个高度不包括padding
,所以计算高度时候需要加上padding
单行文字省略效果
小程序里有一个排行榜的页面,每条记录可能存在名称或者数值溢出的情况,可以考虑使用 css 实现文字溢出显示省略号的功能,不过需要注意使用条件
text-overflow 用于确定如何提示用户存在隐藏的溢出内容,但需要为容器设置准确宽度

从上图分析,内容区域宽度为蓝色区域的宽度,左边文字部分和右边数字部分可能存在溢出情况,就需要给这两个部分添加溢出样式
css
.overflow-style {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
还需要设置准确的宽度,溢出效果才能生效,右边可以设置固定宽度100rpx
,左边宽度如何计算呢,左边文字区域宽度可以用总宽度100%
- 奖牌宽度 - 头像宽度,结果就是文字宽度
由于左边部分存在层级嵌套关系,我们可以逐层计算得出文字宽度
.left
计算出左边宽度width: calc(100% - 110rpx)
,多 10rpx 为了在左右区域之间加个间隔
.info
计算出除奖牌宽度,剩余宽度width: calc(100% - 60rpx)
.name
计算出除头像宽度,剩余文字宽度width: calc(100% - 70rpx)
示例部分省略代码如下
vue
...
<view v-for="(item, index) in otherRankList" class="item">
<view class="left">
<template v-if="index < 3">
<image :src="getMedal(index)" mode="aspectFit" class="medal" />
</template>
<template v-else>
<view class="rank">{{ index + 1 }}</view>
</template>
<view class="info">
<image class="avatar" :src="item.avatar" mode="scaleToFill" />
<view class="name">{{ item.name }}</view>
</view>
</view>
<view class="right">{{ item.value }}</view>
</view>
...
.other-rank {
border-radius: 40rpx 40rpx 0 0;
width: 100%;
height: 100%;
padding: 10rpx 54rpx;
box-sizing: border-box;
position: relative;
z-index: 2;
.item {
@include flex-center;
position: relative;
justify-content: space-between !important;
gap: 10rpx;
height: 110rpx;
width: 100%;
.left {
@include flex-center;
justify-content: flex-start !important;
gap: 20rpx;
width: calc(100% - 110rpx);
.medal {
width: 40rpx;
height: 50rpx;
}
.rank {
width: 40rpx;
text-align: center;
font-size: 32rpx;
font-weight: 700;
color: #B4B6C0;
}
.info {
@include flex-center;
justify-content: flex-start !important;
flex: 1;
gap: 20rpx;
width: calc(100% - 40rpx);
.avatar {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
}
.name {
width: calc(100% - 50rpx);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.right {
width: 100rpx;
text-align: right;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
注意
计算宽度也要把gap
考虑在内,如果忽略了gap
,在文字溢出时候,图片宽度会被压缩,这时候有两种方法解决
- 正确计算对应区域宽度,如果有
gap
,减去gap
宽度 - 在被压缩的容器上添加样式
flex-shrink: 0
,在宽度不足时,禁止压缩
小程序动态样式
页面内如果绑定动态样式,类如
vue
<template>
<view :style="customStyle"></view>
</template>
<script>
export default {
data() {
return {
customStyle: {
fontSize: '16px'
}
}
}
}
</script>
这种写法编译在微信平台并不能生效,正确写法是:style="[customStyle]"