前言
最近在做RN的一个项目,发现一个问题,就是我在本手机上跑的时候样式好好的,换到了模拟器上发现样式发生了变化,所以就想着是RN的适配问题。
好的,接下来开始记录吧。
基本名词
适配:
假设有一个元素在375X667的设计图上标注为375X44,即宽度占满整个屏幕,高度44px。如果我们做好了RN的屏幕适配,那么:
在iPhone 6/7/8(375X667)机型与iPhone X(375X812)机型上,此元素渲染结果会占满屏幕宽度;
在iPhone 6/7/8 Plus(414X736)机型上,此元素渲染结果也应占满屏幕宽度
- 适配:不同屏幕下,元素显示效果一致
- 屏幕尺寸:指的是屏幕对角线的长度
- px(单位): px实际是pixel(像素)的缩写。
- 分辨率 :是指宽度上和高度上最多能显示的物理像素点个数
- 设备物理像素:指设备能控制显示的最小物理单位,意指显示器上一个个的点。从屏幕在工厂生产出的那天起,它上面设备像素点就固定不变了,和屏幕尺寸大小有关
- 设备独立像素(设备逻辑像素):计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如: css像素),这个点是没有固定大小的,越小越清晰,然后由相关系统转换为物理像素
- CSS 像素 : css px 和物理像素的对应关系, 与 viewport 的缩放有关 scale = 1/dpr 时 1px 对应一个 物理像素
- DPI:印机可以在一英寸内打多少个点。每inch 多少个点
- PPI:屏幕上每英寸可以显示的像素点的数量,即屏幕像素密度。
- DPR:设备像素比 = 设备物理像素 / 设备独立像素(CSS像素,一般设计师给的设计图)。
为什么react native没有单位? React Native 是跨平台的,iOS的逻辑像素单位是 pt,Android的逻辑像素单位是 dp,没办法兼顾,就直接去掉了,运行在哪个平台,就用哪个平台的单位。 RN 提供的就是 逻辑像素单位,是和像素密度无关的,RN本身会根据像素密度做适配,渲染对应的物理像素。
比例
为什么要设置比例:保证在页面上元素大小都是按设计图进行等比例缩放。
比例:设备宽度逻辑像素/设计图宽度
在设备上元素的大小:比例 * 元素大小(ps:这个元素大小为设计稿的px元素)
其实所有的适配都是围绕一个比例在做,如web端缩放、rem适配、postcss plugin 等,大道万千,殊途同归!
局部盒子缩放
RN上的元素大小、位置设计尺寸的地方,要按照上面的比例进行计算。
布局适配
布局,保证页面元素大小按照设计稿尺寸进行等比例缩放。
//获取设备dp
const designTotalWidth = 1920; // 设计稿尺寸 1920px*1080px
const {deviceWidth, deviceHeight} = Dimensions.get('window');
//设计稿上一个 640px*540px 的区域。CSS
{
width: (640/designTotalWidth) * deviceWidth
height: (540/designTotalWidth) * deviceWidth
}
//得到px转换后的在各个手机上的适配
export const transformAdaption = (element: number) => {
const scale = Dimensions.get('window').width / UiWidth
return scale * element
}
文字适配
包装的文字适应组件,别的组件使用这个组件就可以了
import React, { memo } from 'react'
import type { FC, ReactNode } from 'react'
import { Text, PixelRatio, Dimensions } from 'react-native'
import { transformAdaption } from '../../utils/adaptation'
interface IProps {
children?: ReactNode
style: any
fontSize?: number
}
const AutoText: FC<IProps> = ({ children, style, fontSize, ...props }) => {
//PixelRatio.get() 得到的是设备的像素密度
const size = PixelRatio.get() * transformAdaption(fontSize ? fontSize : 15)
return (
<Text style={[style, { fontSize: size }]} {...props}>
discover
</Text>
)
}
export default memo(AutoText)
图像
使用resizeMode属性: 在Image组件中,使用resizeMode属性来指定图片的缩放模式。适配的时候可以考虑使用 cover、contain 或者 stretch 等模式。
<Image source={require('./path/to/image.png')} style={{ width: 100, height: 100, resizeMode: 'cover' }} />
边框和线条
边框和线条一般要求所有屏幕保持相同的px 显示,比如需要看到一条最细的边框线。
//边框和线条
export const myBorderWidth = (width = 1) => {
return width / PixelRatio.get()
}
//最小的border
width:1/PixelRatio.get()
一些疑惑的点
DimensionsApi的screen和window
const {windowWidth,windowHeight} = Dimensions.get('window');
const {screenWidth,screenHeight} = Dimensions.get('screen');
Dimensions.get('window')——获取视口参数width、height、scale、fontScaleDimensions.get('screen')——获取屏幕参数width、height、scale、fontScale
其中,在设备屏幕同状态的默认情况下screen的width、height永远是≥window的width、height,因为,window获取的参数会排除掉状态栏高度(translucent为false时)以及底部虚拟菜单栏高度。 当此安卓机设置了状态栏translucent为true并且没有开启虚拟菜单栏时,Dimensions.get('window')就会与Dimensions.get('screen')获取的width、height一致,否则就不同。这就是本段开始时有时相同,有时又有差异的问题的答案。
最后
引用该博客的: