浅析小程序响应式像素实现原理

2021-07-06 11:44:02 浏览数 (1)

本次分享我们来谈谈微信小程序的响应式像素是如何实现的。

# 官方文档说明

WXSS (WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式。

WXSS 用来决定 WXML 的组件应该怎么显示。

为了适应广大的前端开发者,WXSS 具有 CSS 大部分特性。同时为了更适合开发微信小程序,WXSS 对 CSS 进行了扩充以及修改。

# 尺寸单位

  • rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

那么在 iPhone6 的环境下,为什么写了2rpx就会等同于1px呢?

带着疑问我们可以在解包之后的文件中找到答案。

首先如果你读过博主前面的文章,应该会知道小程序的本质是一个混合应用,是一堆js代码,页面结构及业务逻辑都是打包为js的,然后wxss样式本身也是用服务端的编译器去打包为js的,所以我们就可以从源码上略知一二了。

如果你试着去寻找一下,会发现类似如下的代码。

  • 这里我们节选一部分代码来帮助理解所谓响应式像素的实现过程。
代码语言:javascript复制
var BASE_DEVICE_WIDTH = 750;
var isIOS = navigator.userAgent.match('iPhone');
var deviceWidth = window.screen.width || 375;
var deviceDPR = window.devicePixelRatio || 2;
var checkDeviceWidth =
window.__checkDeviceWidth__ ||
function () {
  var newDeviceWidth = window.screen.width || 375;
  var newDeviceDPR = window.devicePixelRatio || 2;
  var newDeviceHeight = window.screen.height || 375;
  if (
    window.screen.orientation &&
    /^landscape/.test(window.screen.orientation.type || '')
  )
    newDeviceWidth = newDeviceHeight;
  if (newDeviceWidth !== deviceWidth || newDeviceDPR !== deviceDPR) {
    deviceWidth = newDeviceWidth;
    deviceDPR = newDeviceDPR;
  }
};
checkDeviceWidth();
var eps = 1e-4;
var transformRPX =
window.__transformRpx__ ||
function (number, newDeviceWidth) {
  if (number === 0) return 0;
  number =
    (number / BASE_DEVICE_WIDTH) * (newDeviceWidth || deviceWidth);
  number = Math.floor(number   eps);
  if (number === 0) {
    if (deviceDPR === 1 || !isIOS) {
      return 1;
    } else {
      return 0.5;
    }
  }
  return number;
};

下面我们来一起看下吧~ 小程序页面在注册的过程中会定义一些变量:

  • BASE_DEVICE_WIDTH,值为750,也就是官网所说的基准设备宽度(规定屏幕宽为750rpx)。
  • deviceWidth,取值为屏幕宽度,默认值375。
  • deviceDPR,设备上物理像素和逻辑像素的比例,所说的像素密度,默认为2。

另外通过用户代理(UA)来判断设备是否为IOS,之后定义了一个全局变量checkDeviceWidth指向的一个挂载在window对象下全局函数__checkDeviceWidth__,如果window下面没这个对象则直接返回一段检查屏幕宽高的函数。如果是横屏情况则会把屏幕宽度设为高度值,重新设置宽高。 之后直接执行了这段函数checkDeviceWidth

再往下就是本次的关键部分了,定义了一个名为transformRPX的函数,看意思就知道了,作用是转化rpx单位的,该函数支持传入两个参数:

  • number
  • newDeviceWidth

这里大家应该明白了,传入的第一个参数就是我们手写的wxss样式某标签的具体宽高值,第二个则是设备宽度。 函数内容也大致说下吧:

  • 如果传的是0,比如0rpx,那转换之后自然就是0了
  • 如果不是 0,则按公式((number / BASE_DEVICE_WIDTH) * (newDeviceWidth || deviceWidth))做了换算,就是按照传入值与基准设备宽度得到的比率乘以当前设备宽度
  • 并且这里利用1e-4的eps值做了小量比较,即传入的number值转换之后加上eps并向下取整了。
  • 并且取整之后为0的情况,如果像素密度为1或者是非IOS设备则返回1,像素密度如果大的,或者IOS的设备就返回了0.5。

大致的作用就解析到这里吧,页面样式最终转换后还是以px为单位进行渲染的。

所以会有同学又有疑问了,小程序运行环境中的webview下面到底认不识rpx呢?

这里其实大家应该大概了解微信小程序的是如何根据屏幕宽度进行自适应显示的思路了。

当然这里只是简要介绍了一下,真实情况要比这个复杂,我们从视图层基础库的代码中也可以窥探到一些有意思的东西,比如组件占位符的样式,内联样式的转换等等(有兴趣的同学可以试着在基础库的 WAWebview.js 文件中去搜索一下 transformRpx)。

# 参考资料

  • wxss
  • Math.floor()
  • 使用 epsilon 比较浮点数

0 人点赞