web移动端适配方案之rem适配
相关标签
rem、viewport、mediaQuery、vw,vh、...
前言
最近空余时间,还是在找些react小项目练手。练手的项目不是别的,正好是目前在做的iOS项目,在边做iOS边写react的对比中,自然而然地牵扯到了webApp的适配问题。
一、移动端适配前期准备
meta标签设置ideal viewport
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
meta viewport 标签首先是由苹果公司在其safari浏览器中引入的,目的就是解决移动设备的viewport问题。因为好用后面的各种浏览器都纷纷对此进行了兼容。这里就不解释viewport的方方面面了,这篇文章解释的很详细。移动前端开发之viewport的深入理解
二、理解rem是什么
“font size of the root element”,W3C官网是这样定义的。直白点说就是根节点元素,(一般为<html>标签)的fontSize。
打个比方:
如果根节点的元素fontSize设为100px,意味着1rem=100px。那么我们如果有一个边距在UI标注中为200px,就可以写为2rem。
嗯...很好理解。这也是我查看N多前端童鞋写的相关rem文章时他们主要提到的。问题是,这和传说中适配有什么关系?
别着急,耐心看下去~
三、先来谈一下iOS端的比例适配
直接看代码,以下是我在iOS端处理的适配方案:
OC代码:
/**
* 375为UI标注采用的屏幕宽度标准
* 实际屏幕宽 / 375,获取真实尺寸与UI尺寸比例
*/
#define BEI6 (MIN(Screen_Width, Screen_Height) / 375)
/**
* size为UI标准的期望值
* 根据真实尺寸与UI尺寸的比例,计算出期望值的真实尺寸
*/
#define AutoSize(size) ((size) * BEI6)
/**
* 根据UI标注的frame获取真实frame
*/
#define AutoFrame(frame) CGRectMake(frame.origin.x*BEI6,frame.origin.y*BEI6, frame.size.width*BEI6, frame.size.height*BEI6)
swift代码:
public func kBEI6() -> CGFloat {
return min(kScreenWidth, kScreenHeight) / 375.0
}
public func kAutoSize(size: CGFloat) -> CGFloat {
return kBEI6() * size
}
public func kAutoFrame(frame: CGRect) -> CGRect {
return CGRect(x: (kBEI6() * frame.origin.x), y: (kBEI6() * frame.origin.y), width: (kBEI6() * frame.size.width), height: (kBEI6() * frame.size.height))
}
代码很简洁,很容易就能明白。
就是通过手机真实屏幕的宽度除以UI标注上的宽度尺寸,获得一个比例。再以这个比例为媒介获取每一个期望值在真实手机上的真实尺寸。
实际上rem适配的精髓和这种iOS适配方式如出一辙!
题外话:非移动端的童鞋可以忽略!!!
移动端原生控件的宽高其实并不是指单纯的像素,因为有retina屏的原因,所以原生中的1 有可能为2px或3px。这就导致了原生控件中假如给定宽为100,实际上在相同物理屏幕宽度下,其有可能是200像素,也有可能是300像素。移动端懂得自然懂。6s和6plus就是个极为典型的例子,其屏幕宽度都是414point。但其一个为2倍屏,即宽度为828像素;一个为3倍屏,宽度为1242像素。
三、rem是如何进行比例适配的
子标题这样说,细究起来是不对的。因为rem只是一个单位,1rem=根节点的fontSize值而已。其本身是不具备适配功能的。
但是其与根节点的fontSize有这种特殊关系,那我们大可以对根节点的fontSize做做手脚~通过动态设置根节点fontSize,即动态设置单位rem值,实现比例适配。
刚才说了rem适配其实与我那种iOS比例适配有异曲同工之妙,iOS适配的比例因子是真实屏幕宽度与UI标注宽度的商。那在我们web端,如果获取到浏览器的真实宽度(webApp中就是webView宽度),通过其与UI标注的宽度进行除法运算,商就是我们需要的适配比例因子。再通过这个比例因子,动态比例地设置根标签的fontSize,从而达到1 rem值对应像素单位px的比例升降。
知道了步骤,我们编写代码如下:
请仔细阅读注释,句句精髓
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<title>rem适配</title>
<!--因为是设置样式的js代码,所以写在head标签中了-->
<script type="text/javascript">
//自执行匿名函数
(function (uiWidth, expectFontSize) {
//获取浏览器真实宽度
let deviceWidth = document.documentElement.clientWidth
function setRemUnit() {
//计算适配比例
let scale = deviceWidth / uiWidth
//乘以100的目的是,这样我们将UI标注的px值除以100,就是我们需要的rem值
document.documentElement.style.fontSize = scale * expectFontSize + 'px'
}
//执行初始rem函数
setRemUnit()
//设置监听,当window重新resize的时候,重新初始rem
window.addEventListener('resize', setRemUnit)
})(375, 100)
</script>
</head>
以上代码中的375,是假设的UI标注的标准宽度。依具体的UI标注进行替换。
核心思想就是设备屏幕宽度不同,比例因子不同。从而使得根标签的fontSize不同,也就是单位rem对应的px值不同。 1rem = 比例因子 * 默认期望值
我们根据上面的代码捋一下产生的结果:
- 假如iPhone6设备(屏幕宽度为375px)上,设置一个控件宽度设为0.7rem,此时换算为px单位为(375 / 375.0 * 70) = 70px;
- 而在iPhone8设备(屏幕宽度为414px)上,0.7rem换算为px单位为(414 / 375.0 * 70) = 77.28px。这样就很完美地实现了比例适配功能。
四、媒体查询对特殊情况进行适配
做iOS开发的时候,使用上面我提到的比例适配方案,有时候并不能完美地去进行适配。比如处理刘海屏、适配iOS11等情况。这个时候命令式的iOS语法会单独地对这些情况去做处理。那么前端语言要怎么去解决这个问题呢?
- 第一种最直白的方法就是利用js获取真实dom,然后针对特殊情况去设置样式属性,显然这种面向过程的方式太low太繁琐。
- 利用媒体查询mediaQuery的方式进行处理,语法如下
@media 媒体类型 and (媒体特性: 600px) {
选择器 {
属性:属性值;
}
}
具体的媒体类型和特性可以在W3C官网上查看,我个人觉得没必要看,会一些常用的。其他到用到时再去查阅即可。
举个例子说明一下:
//处理屏幕宽小于等于320px时,类名为inner的标签样式
@media screen and (max-width: 320px) {
.inner {
height: 100%;
width: 25%;
float: left;
}
}
//处理屏幕宽大于等于321px时,类名为inner的标签样式
@media screen and (min-width: 321px) {
.inner {
height: 50px;
width: 100%;
}
}
五、vh、vw方案适配
这种好像算是目前最流行的移动端适配方案,暂时还没接触。待定...