一、相关概念
rem(font size of the root element)是指相对于根元素(html)的字体大小的单位。简单的说它就是一个相对单位。em(font size of the element)是指相对于父元素的字体大小的单位。它们之间其实很相似,只不过一个计算的规则是依赖根元素一个是依赖父元素计算。
设备物理像素:通俗的讲设备屏幕有多少个可以闪烁的点 是一个具体的概念 比如iphone6横向就有750个可以改变颜色的点 类似与电视机 如果家里有10年前买的大头电视,你趴在屏幕前仔细看能看到一个个RGB的点 这就是设备的物理像素。
设备独立像素:设备独立像素是一个虚拟的概念,如程序中的css 比如我们将一个div宽度设置为10像素 那么在pc上系统会将这个div显示在屏幕的10个点上
dpr : 设备物理像素 / 设备独立像素
- iphone3G 设备物理像素320个点 设备独立像素320px 那么dpr就是1
- iphone6 设备物理像素750个点 设备独立像素375px 那么dpr就是2
- iphoneX 设备物理像素1125个点 设备独立像素375px 那么dpr就是3
二、需要解决的问题
1、字体大小需要在根据不同手机屏幕大小适配
2、图片需要根据不同手机的dpr进行适配(解决屏幕高清图片问题)
3、解决移动端0.5px细线问题。
三、解决方法
1、根据不同手机的屏幕大小和dpr设置html根字体大小,使用rem布局。
2、设置页面窗口的缩放比例。
function flex (baseFontSize, normal, fontscale) {
const win = window
const REM_BASE_SIZE = 100
const _baseFontSize = baseFontSize || REM_BASE_SIZE
// const _fontscale = fontscale || 1
const doc = win.document
const ua = navigator.userAgent
const matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i)
const UCversion = ua.match(/U3\/((\d+|\.){5,})/i)
const isUCHd = UCversion && parseInt(UCversion[1].split('.').join(''), 10) >= 80
const isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi)
let dpr = win.devicePixelRatio || 1
if (!isIos && !(matches && matches[1] > 534) && !isUCHd) {
// 如果非iOS, 非Android4.3以上, 非UC内核, 就不执行高清, dpr设为1;
dpr = 1
}
const scale = normal ? 1 : 1 / dpr
localStorage.setItem('initial-scale', scale)
let metaEl = doc.querySelector('meta[name="viewport"]')
if (!metaEl) {
metaEl = doc.createElement('meta')
metaEl.setAttribute('name', 'viewport')
doc.head.appendChild(metaEl)
}
const currentScreenW = window.screen.availWidth || 375
console.log(currentScreenW)
metaEl.setAttribute('content', `width=device-width,user-scalable=no,initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale}`)
doc.documentElement.style.fontSize = normal ? '50px' : `${_baseFontSize / 2 * dpr * currentScreenW / 375}px`
let _fontscale_ = 1
if (currentScreenW < 375) {
_fontscale_ = currentScreenW / 375
doc.documentElement.style.fontSize = normal ? '50px' : `${_baseFontSize / 2 * dpr * currentScreenW / 375}px`
}
localStorage.setItem('font-scale', _fontscale_)
}
export default flex(75)
四、注意事项
1、入口的html文件不需要再设置mata|viewport标签,上述代码会根据手机的dpr动态生成。
2、第三方插件默认按dpr=1的情况书写样式,会导致页面布局整体缩小。解决方法如下:
- 使用px2rem-loader将插件的px转化为rem,前提是px2rem-loader设置的remUnit应当为rem基准的一半。比如我们设置iphone6(dpr=2)的根字体为为100px,px2rem-loader设置的remUnit就是50。我们目前设置的iphone6(dpr=2)的根字体为为75px(参照UI设计图为375),px2rem-loader设置的remUnit就是37.5。
- 当第三方插件使用的是行内样式时,px2rem-loader也不能把px转化为rem。(这种情况不多,后续继续研究,如遇到,少量样式可手动修改处理)
3、img的width和height需要写在内联样式里,如需要写行内样式,需要乘以dpr。
4、echart的整体宽高可以设置成rem,与文案字体(如标题,坐标轴文字等)大小和根据html根字体进行百分比转化。
- 如设置的iphone6(dpr=2)根字体为为75px,UI设计稿(375px)标题字体为18px,则可以设置echart的标题字体参数为18*2/75=48%。
五、以前采坑
以前使用的rem适配方法如下:主要使用了lib-flexible和px2rem-loader。
1、下载并引入lib-flexible,根据不同手机的dpr动态生成mata|viewport标签
2、使用px2rem-loader将px转化为rem,包括自己写的样式及第三方插件样式(行内样式除外)
3、对于蓝湖375px的设计稿,设置px2rem-loader的转化标准的37.5,可解决第三方插件的样式问题。
但存在两个误区:
1、lib-flexible能解决不同型号苹果手机的dpr问题,但安卓没做处理,默认安卓机drp=1。
2、在处理一个旧项目问题(未使用rem)的一个解决方案是在入口html文件手动创建了mata|viewport标签,写死了缩放比例,也就是dpr=1,导致lib-flexible没生效,当时还以为这方法完美地解决了第三方插件的样式问题。但是图片的高清问题得不到解决。
六、后续开发建议
1、引入flexible.js,设置基准为1rem=100px(iphone6),设置UI设计稿为rem/px为50,自己写的样式统一使用rem,第三方组件使用px2rem-loader将px转化为rem,remUnit设置值为50。1px的细线可以写成0.02rem。
2、引入flexible.js,设置基准为1rem=75px(iphone6),设置UI设计稿为rem/px为37.5,自己写的样式统一使用px,第三方组件使用px2rem-loader将px转化为rem,remUnit设置值为37.5。1px细线使用伪类元素生成。