在微信小程序里使用rpx,被坑了 | 不同设备表现不同

补给资料    管注公众号:码农补给站     

小小需求

实现一个 Tooltip,就是那种很简单有一个按钮,按一下就会出现或消失气泡的组件,就是下面这个效果。

放个按钮、加个点击事件、做好气泡的定位好像就搞定了。然鹅,事情没有发展的这么顺利。

开发

组件结构

按照上面说的过程,放好按钮,添加好点击事件。比较复杂的地方就是处理气泡的定位,气泡需要进行绝对定位,让它脱离文档流,不能在隐藏或偏移的时候还占个坑(需要实现那种浮动的效果)。还有气泡有个小三角,这个三角也是需要额外处理定位的。于是设计了组件的结构如下:

old.png

Tooltip 包着 Button 显示在界面上,设置定位属性,让它可以成为子元素定位的基准元素。然后创建 Prompt,它会相对于 Tooltip 进行定位,Prompt 中的小三角形则相对于 Prompt 进行定位。定位的具体数值则根据元素的尺寸和想放置的位置进行定位。在这个例子中就是实现气泡在按钮下方居中显示, Prompt 偏移数值计算如下:
水平偏移需要考虑 Tooltip 和 Prompt 的宽度,偏移的距离就是两者宽度之差的一半。

left=width(Tooltip)/2−width(Prompt)/2left = width(Tooltip) / 2 - width(Prompt) / 2left=width(Tooltip)/2−width(Prompt)/2
垂直偏移则要考虑 Tooltip、Prompt 和 小三角的高度,计算方法类似。(偷懒不做动图了)
top=height(Tooltip)+hegiht(::before_border)+height(gap)top = height(Tooltip) + hegiht(::before\_border) + height(gap)top=height(Tooltip)+hegiht(::before_border)+height(gap)
小三角相对于气泡的偏移也是类似的计算方法,总之能够根据元素的尺寸让偏移刚好能够居中。

代码

代码如下:(wxml 和 wxss 没有高亮,用 html 和 css 格式代替了)

<view class="tooltip" >
	<button size="mini" bindtap="handleTooltipShow">TOOLTIP</button>
	<view wx:if="{{showTooltip}}" class="prompt-container">
		<view class="prompt-title">这是弹窗标题</view>
		<view class="prompt-content">这是弹窗内容,啊吧啊吧吧</view>
	</view>
</view>
 
.tooltip{
	display: flex;
	align-items: center;
	position:relative;
	width:350rpx;
	height: 64rpx;
}
.prompt-container{
	background-color: rgba(0,0,0,0.7);
	border-radius: 16rpx;
	color: #fff;
	width: 200rpx;
	
	position:absolute;
	padding: 20rpx;
	top: 80rpx;
	left: 55rpx;
}
.prompt-container::before{
	position:absolute;
	content:'';
	width: 0rpx;
	height: 0rpx;
	border: 14rpx solid transparent;
	border-bottom-color:rgba(0,0,0,0.7);
	top: -28rpx;
	left: 103rpx;
}
.prompt-title{
	font-size: 26rpx;
	font-weight: bold;
}
.prompt-content{
	font-size: 24rpx;
}

踩坑

美滋滋呀,这就做完了,在 iPhone 12 mini 模拟器上看起来丝毫没有问题,整个气泡内容看起来是那么完美~
 

12mini


换个最豪(ang)华(gui)的机型(iPhone 14 Pro Max)看看,大事不妙,怎么小三角和气泡之间出现了一条缝,再看看代码按按计算器,算的尺寸没有任何问题呀!但是展示就是变成了这样:
 

14pm

爬坑

打开调试器一顿猛调试,发现了一些不对劲,下面慢慢说。

关于 rpx

微信小程序提供了个特殊的单位 rpx,代码中也都是使用这个单位进行开发,据说是能够方便开发者在不同尺寸设备上实现自适应布局。 放一张官方文档截图:

wxwd.png

它的意思就是,它把所有的屏幕宽度都设置为 750rpx,不管这个设备真实的宽度有几个设备独立像素(就是宽度有多少 px)。开发者只需要使用 rpx 为单位,小程序会帮你把 rpx 转成 px,听起来是不是很方便很友好~(但并不是🌚)

试试这个公式是不是真的

根据图中提供的转换方式 1rpx=(screenWidth/750)×px1rpx = (screenWidth / 750) \times px1rpx=(screenWidth/750)×px, 用上面那个例子中的 Tooltip 组件来进行验证,手动算一下在设备上得到的 px 值是不是真的能用上面的公式计算出来。

设备屏幕尺寸 / 750组件理论尺寸真实尺寸
iPhone 12 mini375 / 750 = 0.5Tooltip175 × 32175 × 32
iPhone 14 Pro Max428 / 750 = 0.57Tooltip199.5 × 36.48199× 36

看起来真实的计算会直接省略小数点后的值,直接进行取整。这可能是导致我们的预期和真实展示有偏差的原因。再看看其他组件的尺寸计算:

设备屏幕尺寸 / 750组件理论尺寸真实尺寸
iPhone 12 mini375 / 750 = 0.5Prompt120120
::before14 × 1414 × 14
iPhone 14 Pro Max428 / 750 = 0.57Prompt136.8136
::before15.96 × 15.9614 × 14

因为高度是根据文字自适应的,所以这里没有计算 Prompt 的高度。但依然可以从表中看出,rpx 到 px 的转换并不是简单的直接取整,不然 iPhone 14 Pro Max 中的 ::before 应该尺寸为 15 × 15。至于到底是所有 rpx 到 px 转换都有隐藏的规则,还是伪元素的尺寸转换和其他元素不统一,还是 border 尺寸计算比较特殊,我们也无从得知,官方也没有相关说明。

小三角相对于气泡的偏移

其实在这个例子中,我们最关心的就是这个小三角相对于气泡的偏移是不是符合预期,整体气泡居中与否那么小的差别我们几乎看不出来,但是这个小三角偏离气泡这段距离,搁谁都无法接受。 那着重看下这个小三角的偏移我们是怎么做的,小三角的尺寸完全是由边构成的,完整的矩形尺寸是 28rpx×28rpx28rpx × 28rpx28rpx×28rpx,我们向上的偏移需要设置成整个矩形的尺寸,也就是 top:−28rpxtop: -28rpxtop:−28rpx,这样才能让下半部分的小三角完全展示出来。理论上来说,尺寸和偏移都设置 rpx 为单位,如果使用统一的转换规则,那肯定也是没问题的,既然出现了问题肯定是两者的计算不是那么的统一。我们看到实际的结果,尺寸计算是不符合我们的预期的,那么就猜测偏移可能是按照公式计算的。可以来验证一下,计算得到的值 151515 和真实值 141414 相差 1px1px1px,我打算放个高度为 1px1px1px 的长条在这个缝隙里,看看是不是刚好塞进去。

test.png


竟然真的刚好塞进去了,这说明我的猜测应该没有错,偏移的计算在我们预期中,小三角向上移动了 15px。但不能进行更多的验证了,再猜我就要把这个规律猜出来了(手动狗头🤪)。 总之就是,盒子尺寸的计算和偏移距离的计算用的不是一个规律,这就是坑之所在。

解决方案

针对当前需求

我们可以避开这个坑,让小三角相对于气泡不要产生偏移,而是能死死的贴在气泡上。想要达到这样的效果,我们需要修改一下布局结构,改成下面这个样子:

new.png

让气泡整体和小三角形成兄弟关系,那他俩就不会分离了,然后整体的偏移让他们的父节点 Prompt 来决定。

代码如下:

<view class="tooltip" >
    <button size="mini" bindtap="handleTooltipShow">TOOLTIP</button>
    <view wx:if="{{showTooltip}}" class="prompt-container">
        <view class="prompt-triangle"></view>
        <view class="prompt-text-container">
            <view class="prompt-title">这是弹窗标题</view>
            <view class="prompt-content">这是弹窗内容,啊吧啊吧吧</view>
        </view>
    </view>
</view>
 

.tooltip{
    display: flex;
    align-items: center;
    position:relative;
    width:350rpx;
    height: 64rpx;
}
.prompt-container{
    position:absolute;
    top: 80rpx;
    left: 55rpx;
}
.prompt-triangle{
    position:relative;
    content:'';
    width: 0rpx;
    height: 0rpx;
    border: 14rpx solid transparent;
    border-bottom-color:rgba(0,0,0,0.7);
    left: 103rpx;
}
.prompt-text-container{
    background-color: rgba(0,0,0,0.7);
    border-radius: 16rpx;
    color: #fff;
  width: 200rpx;
  padding: 20rpx;
}
.prompt-title{
    font-size: 26rpx;
    font-weight: bold;
}
.prompt-content{
    font-size: 24rpx;
}

通用方法

除了上面避坑的方法,还有一个方法就是进行一个填坑。自定义实现一个 rpx2px 方法,动态的根据设备来进行 px 值的计算,再通过内联样式传递给元素。

function rpx2px(rpx){
    return ( wx.getSystemInfoSync().windowWidth / 750) * rpx
}
Page({
  data: {
    top: rpx2px(100), // 类似这样定义一个状态,通过内联样式传入
  },
})
 
 

<view class="tooltip" >
    <button size="mini" bindtap="handleTooltipShow">TOOLTIP</button>
    <view wx:if="{{showTooltip}}" class="prompt-container">
        <view class="prompt-triangle" style="top:{{top}}px"></view>
                            <!-- 注意上面👆这里添加了 style 内联样式 -->
        <view class="prompt-text-container">
            <view class="prompt-title">这是弹窗标题</view>
            <view class="prompt-content">这是弹窗内容,啊吧啊吧吧</view>
        </view>
    </view>
</view>

这样子不管是什么尺寸什么偏移都按照统一的规则进行换算,妈妈再也不同担心我被坑啦~~~

总结

虽然解法看起来如此简单,但是爬坑的过程真是无比艰难,各种猜测和假设,虽然一些得到了验证,但最终也是无法猜透小程序的心~~ 只能自己避坑和填坑,按需选择吧。


原文链接:https://juejin.cn/post/7257516901843763257
 

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值