移动端如何适配各种手机机型?

有哪些比较好的写法,或者框架及其优缺点
已邀请:

梦旅人

赞同来自:

rem+flex+媒体查询
rem布局
<script>
/*实际宽度除以30*/
var html = document.getElementsByTagName("html")[0];
var rootResize = function() {
var winClient = document.documentElement.clientWidth;
var fontSize = winClient < 480 ? winClient / 16 : 30;
if (fontSize < 20) {
fontSize = 20;
}
html.style.fontSize = fontSize + "px";
}
rootResize();
window.onresize = function() {
rootResize();
}
/*实际宽度除以100*/
(function (doc, win) {
var docEl = doc.documentElement;
function recalc() {
docEl.style.fontSize = 100 * ((docEl.clientWidth > 640 ? 640 : docEl.clientWidth) / 640) + "px";
}
resizeEvt = "orientationchange" in window ? "orientationchange" : "resize";
win.addEventListener(resizeEvt, recalc, false);
recalc();
})(document, window);
</script>
flex布局
解决传统布局不方便的特殊布局,比如,垂直居中。flex布局,可以简便、完整、响应式地实现各种页面布局。

必要时加媒体查询进行各个手机适配的细节调整
 

hardy - 90后IT男

赞同来自:

移动端适配总结

 视口 viewport

viewport 解释为中文就是‘视口’的意思,也就是浏览器中用于显示网页的区域。在 PC 端,其大小也就是浏览器可视区域的大小,所以我们也不会太关注此概念;而在移动端,绝大多数情况下 viewport 都大于浏览器可视区,保证 PC 页面在移动浏览器上面的可视性。为提升可视性体验,针对移动端有了对 viewport 的深入研究。
 
viewport 详解
 
在移动端有三种类型的 viewport: layoutviewport、visualviewport、idealviewport。具体解释如下:
  • layoutviewport: 大于实际屏幕, 元素的宽度继承于 layoutviewport,用于保证网站的外观特性与桌面浏览器一样。layoutviewport 到底多宽,每个浏览器不同。iPhone 的 safari 为 980px,通过 document.documentElement.clientWidth 获取。
  • visualviewport: 当前显示在屏幕上的页面,即浏览器可视区域的宽度。
  • idealviewport: 为浏览器定义的可完美适配移动端的理想 viewport,固定不变,可以认为是设备视口宽度。比如 iphone 7 为 375px, iphone 7p 为 414px。

 
viewport 设置 

我们通过对几种 viewport 设置可以对网页的展示进行有效的控制,在移动端我们经常会在 head 标签中看到这段代码:
<meta name='viewport' content='width=device-width,initial-scale=1,user-scale=no' />

通过对 meta 标签三个 viewport 的设置,最终使页面完美展示。下面详细的阐释其具体含义: 
  • width 设置的是 layoutviewport 的宽度
  • initial-scale 设置页面的初始缩放值,并且这个初始缩放值是相对于 idealviewport 缩放的,最终得到的结果不仅会决定 visualviewport,还会影响到 layoutviewport
  • user-scalable 是否允许用户进行缩放的设置

 
对上面的说明通过公式推导进行进一步的解释:

// 设定两个变量: viewport_1 = width; viewport_2 = idealviewport / initial-scale; // 则: layoutviewport = max{viewport_1, viewport_2}; visualviewport = viewport_2;


只要 layoutviewport === visualviewport,页面下面不会出现滚动条,默认只是把页面放大或缩小。
 
viewport 举例 

以下是通过改变 meta viewport 的几个参数的值来算取不同的 viewport:

QQ截图20190410171002.png

 
以上是针对 iphone 6/7/8 的测试数据,且无论怎么设置 viewport 都具有临界值,即:75 <= layoutviewport <= 10000,75 <= visualviewport <= 1500。
 
为什么要设置 viewport

 viewport 的设置不会对 PC 页面产生影响,但对于移动页面却很重要。下面我们举例来说明:
  1. 媒体查询 @media 响应式布局中,会根据媒体查询功能来适配多端布局,必须对 viewport 进行设置,否则根据查询到的尺寸无法正确匹配视觉宽度而导致布局混乱。如不设置 viewport 参数,多说移动端媒体查询的结果将是 980px 这个节点布局的参数,而非我们通常设置的 768px 范围内的这个布局参数
  2. 由于目前多数手机的 dpr 都不再是 1,为了产出高保真页面,我们一般会给出 750px 的设计稿,那么就需要通过设置 viewport 的参数来进行整体换算,而不是在每次设置尺寸时进行长度的换算。

 
设备像素比 dpr 与 1px 物理像素
 
物理像素(physical pixel)
 
 手机屏幕上显示的最小单元,该最小单元具有颜色及亮度的属性可供设置,iphone6、7、8 为:750 * 1334,iphone6+、7+、8+ 为 1242 * 2208
 
设备独立像素(density-indenpendent pixel) 

此为逻辑像素,计算机设备中的一个点,css 中设置的像素指的就是该像素。老早在没有 retina 屏之前,设备独立像素与物理像素是相等的。
 
设备像素比(device pixel ratio) 

设备像素比(dpr) = 物理像素/设备独立像素。如 iphone 6、7、8 的 dpr 为 2,那么一个设备独立像素便为 4 个物理像素,因此在 css 上设置的 1px 在其屏幕上占据的是 2个物理像素,0.5px 对应的才是其所能展示的最小单位。这就是 1px 在 retina 屏上变粗的原因,目前有很多办法来解决这一问题。
 

1px的物理像素的解决方案 

从第一部分的讨论可知 viewport 的 initial-scale 具有缩放页面的效果。对于 dpr=2 的屏幕,1px压缩一半便可与1px的设备像素比匹配,这就可以通过将缩放比 initial-scale 设置为 0.5=1/2 而实现。以此类推 dpr=3的屏幕可以将 initial-scale设置为 0.33=1/3 来实现。
 
设备像素比 dpr 与 rem 的适配方案

 结合以上部分可以实现 1px 的物理像素这一最小屏幕单位,那在此基础上如可让设计通常提供的 750px 设计稿来完美的适配到多种机型上,使用 rem 是一种解决方式。
 
rem 如何设置 

rem 是相对于根元素 html 的 font-size 来做计算。通常在页面初始化时加载时通过对document.documentElement.style.fontSize 设置来实现。
 
rem 适配规则
 
通过对 initial-scale = 1/dpr 的设置,已将对屏幕的描述从物理像素转化到了物理像素上了,这将是后续推导的基础,且设计稿为 750px。
  1. 物理像素为 750 = 375 * 2,若屏幕等分为 10 份,那么 1rem = 75px,10rem = 750px;
  2. 物理像素为 1125 = 375 * 3,若屏幕等分为 10 份,那么 1rem = 112.5px, 10rem = 1125px;
  3. 物理像素为 1242 = 414 * 3, 若屏幕等分为 10 份,那么 1rem = 124.2px, 10rem = 1242px;

 
因此可推导出 rem 的设定方式:
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';

 下面我们将 750px 下,1rem 代表的像素值用 baseFont 表示,则在 baseFont = 75 的情况下,是分成 10 等份的。因此可以将上面的公式通用话一些:
document.documentElement.style.fontSize = document.documentElement.clientWidth / ( 750 / 75 ) + 'px';

整体设置可参考如下代码:
(function (baseFontSize) {
const _baseFontSize = baseFontSize || 75;
const ua = navigator.userAgent;
const matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i);
const isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi);
const dpr = window.devicePixelRatio || 1;
if (!isIos && !(matches && matches[1] > 534)) {
// 如果非iOS, 非Android4.3以上, dpr设为1;
dpr = 1;
}
const scale = 1 / dpr;
const metaEl = document.querySelector('meta[name="viewport"]');
if (!metaEl) {
metaEl = document.createElement('meta');
metaEl.setAttribute('name', 'viewport');
window.document.head.appendChild(metaEl);
}
metaEl.setAttribute('content', 'width=device-width,user-scalable=no,initial-scale=' + scale + ',maximum-scale=' + scale + ',minimum-scale=' + scale);

document.documentElement.style.fontSize = document.documentElement.clientWidth / (750 / _baseFontSize) + 'px';
})();

 同时为了书写方便可以直接通过 px 布局,然后在打包时利用 pxtorem 库转化为基于 rem 的布局。
 
视口单位适配方案
 
将视口宽度 window.innerWidth 和视口高度 window.innerHeight 等分为 100 份,且将这里的视口理解成 idealviewport 更为贴切,并不会随着 viewport 的不同设置而改变。
  • vw : 1vw 为视口宽度的 1%
  • vh : 1vh 为视口高度的 1%
  • vmin : vw 和 vh 中的较小值
  • vmax : 选取 vw 和 vh 中的较大值

 
如果设计稿为 750px,那么 1vw = 7.5px,100vw = 750px。其实设计稿按照设么都没多大关系,最终转化过来的都是相对单位,上面讲的 rem 也是对它的模拟。这里的比例关系也推荐不要自己换算,使用 pxtoviewport 的库就可以帮我们转换。当然每种方案都会有其弊端,这里就不展开讨论。
 
flexible.js源码分析 

flexible.js是阿里无线前端团队开源的用于移动端适配的库。虽然现在官方都承认可以放弃这个解决方案了,但是了解其中的思想还是很重要的

由于viewport单位得到众多浏览器的兼容,lib-flexible这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport来替代此方案。vw的兼容方案可以参阅《如何在Vue项目中使用vw实现移动端适配》一文。


废话不多说,直接上代码
(function flexible (window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1

// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();

// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}

setRemUnit()

// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})

// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))

由于版本不同,可能大家拿到的代码局部地方有所不同,但是整体思路还是不变的。
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1

// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();

setBodyFontSize这个函数的作用就是设置body标签的fontSize,fontSize的值dpr * 12,这个函数的作用是为了覆盖html的fontSize
// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}

setRemUnit()

首选获取document.documentElement.clientWidth的值,这个值表示当前设备layout viewport的宽度(你可以理解html标签的宽度),在iphone6 7 8下这个值是750,然后将整个视口分成10份,这样每一份的宽度为clientWidth / 10,即1rem = clientWidth / 10,之所以分成10份,完全是方便计算,你也可以随意切分,但是最小值不要小于12,因为在谷歌浏览器中有最小fontSize的限制
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})

这段代码是为了在window触发了resize和pageShow事件之后自动调整html的fontSize值
// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}

这句话的代码是检测0.5px的支持,但是我自己还没弄懂,有哪位同学如果弄明白了,可以在下面发个评论,大家互相学习
还有一点差点忘记了,设置viewport
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

看了下flexible的实现,我自己也简单的实现了下,基本思想同flexible.js一样,只不过我添加了一个自动计算scale的功能 
var docuEl = document.documentElement
var metaEl = document.createElement('meta')
var dpr = window.devicePixelRatio
scale = 1 / dpr
metaEl.setAttribute('name', 'viewport')
metaEl.setAttribute('content', `initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale},user-scalable=no`)
docuEl.setAttribute('data-dpr', dpr)
document.head.appendChild(metaEl)

function resizeFontSize() {
docuEl.style.fontSize = docuEl.clientWidth / 10 + 'px'
}
resizeFontSize()

window.onresize = resizeFontSize
window.onpageshow = function(e) {
if (e.persisted) {
resizeFontSize()
}
}

 说了那么久,那有没有一款移动端适配的框架呢?答案是肯定的

multipages-generator(妈妈再也不用担心移动端适配问题了)

multipages-generator(简称MG)是一个为移动端h5量身打造的网站脚手架,用它可以让你相对的以正确的姿势、迅速的身手、专注于业务,愉快的进行h5开发,以及有配套的快速发布流程。
 
现在在手淘,京东,今日头条,美柚等过亿用户的常见h5网页,他们有更新快,灵活,便于分享和传播的特性。这里有他们中的几个h5的例子:(手淘,美柚)。背后他们开发这些h5的框架是怎么样的呢? 

上菜 multipages-generator! 

multipages-generator 是一个类似express-generator的,一键生成多页h5网站架构模板的npm模块;他主要是为了移动端h5,或者简单的vue,react,angular应用的网站架构模板;该架构模板其实也是淘宝,今日头条,美柚等公司开发h5网站架构的缩影。注意,他是一个架构,是一种移动端解决方案,不是现成可部署的网站模板。

他主要的特点是全而精,全面,开发h5所需的他基本都全了,精是开发出来的网页高性能(未来会更好),手机适配好。具有的特别请移步github查看。
 

梦旅人

赞同来自:

lib-flexible​库解决页面终端适配
lib-flexible是一个制作H5适配的开源库,可以下载相关文件,获取需要的JavaScript和CSS文件:https://github.com/amfe/lib-fl ... r.zip

使用方法
lib-flexible库的使用方法非常的简单,只需要在Web页面的<head></head>中添加对应的flexible_css.js,flexible.js文件:
第一种方法是将文件下载到你的项目中,然后通过相对路径添加:
<script src="build/flexible_css.debug.js"></script><script src="build/flexible.debug.js"></script>
或者直接加载阿里CDN的文件:
<script src="http://g.tbcdn.cn/mtb/lib-flex ... ss.js,flexible.js"></script>
另外强烈建议对JS做内联处理,在所有资源加载之前执行这个JS。执行这个JS后,会在<html>元素上增加一个data-dpr属性,以及一个font-size样式。JS会根据不同的设备添加不同的data-dpr值,比如说2或者3,同时会给html加上对应的font-size的值,比如说75px。
如此一来,页面中的元素,都可以通过rem单位来设置。他们会根据html元素的font-size值做相应的计算,从而实现屏幕的适配效果。
除此之外,在引入lib-flexible需要执行的JS之前,可以手动设置meta来控制dpr值,如:
<meta name="flexible" content="initial-dpr=2" />
其中initial-dpr会把dpr强制设置为给定的值。如果手动设置了dpr之后,不管设备是多少的dpr,都会强制认为其dpr是你设置的值。在此不建议手动强制设置dpr,因为在Flexible中,只对iOS设备进行dpr的判断,对于Android系列,始终认为其dpr为1。
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
flexible的实质
flexible实际上就是能过JS来动态改写meta标签,代码类似这样:
var metaEl = doc.createElement('meta');var scale = isRetina ? 0.5:1;
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');if (docEl.firstElementChild) {
document.documentElement.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
documen.write(wrap.innerHTML);
}
事实上他做了这几样事情:
①动态改写<meta>标签
②给<html>元素添加data-dpr属性,并且动态改写data-dpr的值
③给<html>元素添加font-size属性,并且动态改写font-size的值
 
项目实战
把视觉稿中的px转换成rem
首先,目前日常工作当中,视觉设计师给到前端开发人员手中的视觉稿尺寸一般是基于640px、750px以及1125px宽度为准。
问题来了,如何将设计稿中的各元素的px转换成rem。
以设计稿750px宽度为例,Flexible会将视觉稿分成100份(主要为了以后能更好的兼容vh和vw),而每一份被称为一个单位a。同时1rem单位被认定为10a。针对我们这份视觉稿可以计算出:
1a   = 7.5px;1rem = 75px ;
那么我们这个示例的稿子就分成了10a,也就是整个宽度为10rem,<html>对应的font-size为75px:
这样一来,对于视觉稿上的元素尺寸换算,只需要原始的px值除以rem基准值即可。例如此例视觉稿中的图片,其尺寸是176px * 176px,转换成为2.346667rem * 2.346667rem。
 
总结:
这个库在手淘使用,达到了较为稳定的状态。
不需要考虑如何对元素进行折算,可以根据对应的视觉稿,直接切入。

Belong to my world - 时间,让深的东西越来越深,让浅的东西越来越浅

赞同来自:

1、CSS3 Media Queries
实现原理是:在CSS3中我们可以设置不同类型的媒体条件,并根据对应的条件,给相应符合条件的媒体调用相对应的样式表。其实就如同做了一些if判断一样,找到对应的屏幕大小后就会加载对应的CSS来适配不同的手机大小
一般设置如下:
安卓
/*240px的宽度*/
<link rel="stylesheet" media="only screen and (max-device-width:240px)" href="style240.css" type="text/css" />
/*360px的宽度*/
<link rel="stylesheet" media="only screen and (min-device-width:241px) and (max-device-width:360px)" href="style360.css" type="text/css" />
 /*480px的宽度*/
<link rel="stylesheet" media="only screen and (min-device-width:361px) and (max-device-width:480px)" href="style480.css" type="text/css" />
ios:
@media only screen and (min-device-width: 320px){  
//针对iPhone 3  
}     
@media only screen and (min-device-width: 320px)and (-webkit-min-device-pixel-ratio: 2) {  
 //针对iPhone 4, 5c,5s, 所有iPhone6的放大模式,个别iPhone6的标准模式  
}  
@media only screen and (min-device-width: 375px)and (-webkit-min-device-pixel-ratio: 2) {  
//针对大多数iPhone6的标准模式  
}  
   
@media only screen and (min-device-width: 375px)and (-webkit-min-device-pixel-ratio: 3) {  
//针对所有iPhone6+的放大模式     
}  
@media only screen and (min-device-width:412px) and (-webkit-min-device-pixel-ratio: 3) {  
//针对所有iPhone6+的标准模式,414px写为412px是由于三星Nexus 6为412px,可一并处理     
}
2、rem适配

1) 概念:
rem(font size of the root element)是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。看到rem大家一定会想起em单位,em(font size of the element)是指相对于父元素的字体大小的单位。它们之间其实很相似,只不过一个计算的规则是依赖根元素一个是依赖父元素计算。

2)rem实现原理

rem是通过根元素进行适配的,网页中的根元素指的是html我们通过设置html的字体大小就可以控制rem的大小。举个例子:

html{font-size:10px;}
.btn {width: 6rem;height: 3rem;line-height: 3rem;font-size: 1.2rem;display: inline-block;background: #06c;color: #fff;border-radius: .5rem;text-decoration: none;text-align: center; }

结果:

6687c2d74ed6bae33ddf5f3a2cff5e8e.jpg



把html设置成10px是为了方便我们计算,为什么6rem等于60px。如果这个时候我们的.btn的样式不变,我们再改变html的font-size的值,看看按钮发生上面变化:

html{font-size:40px;}

结果:
6445ffb4b492a9e9622a237383ebf594.jpg


width,height变成了上面结果的两倍,我们只改变了html的font-size,但.btn样式的width,height的rem设置的属性不变的情况下就改变了按钮在web中的大小。

其实从上面两个案例中我们就可以计算出1px多少rem:

第一个例子:
120px = 6rem * 20px(根元素设置大值)
第二个例子:
240px = 6rem * 40px(根元素设置大值)
推出:
10px = 1rem 在根元素(font-size = 10px的时候);
20px = 1rem 在根元素(font-size = 20px的时候);
40px = 1rem 在根元素(font-size = 40px的时候);

3、屏幕适配
屏幕适配最终的目标或者说本质就是实现 等比缩放。
主要分为以下几步:
找到一个基准,基准能随屏幕宽度变化
确定基准的值
根据基准的值来写我们的样式

为什么要有个基准?因为我们不希望每种屏幕写一种布局样式,所以我们需要有一个基准来随屏幕宽度变化,我们只要根据基准来确定我们的css值,就可以适配所有的屏幕了。

基准是什么?为了简单基准我们看成单位,所以我们需要找一个能变化的单位,思考下,css中哪些单位可以变化?rem是以根字体的大小来确定自己的值的,符合条件。所以我们可以让根字体随屏幕变化而变化,我们直接用rem进行布局可以了。
下一步就是确定基准值,我们这里就是确定根字体的值。为了方便我们计算我们可以设置一个很容易计算的值,比如我们可以让设计稿中1rem=30px,那么写起来就是
<script>
/*实际宽度除以30*/
 var html = document.getElementsByTagName("html")[0];
 var rootResize = function() {
 var winClient = document.documentElement.clientWidth;
 var fontSize = winClient < 480 ? winClient / 16 : 30;
  if (fontSize < 20) {
      fontSize = 20;
   }
   html.style.fontSize = fontSize + "px";
   }
   rootResize();
   window.onresize = function() {
   rootResize();
   }
</script>
里边的所有的实际宽度除以30就行了。

京京゚乐道

赞同来自:

解决页面自适应的几种方法(移动端适配方法):
(1)通过媒体查询的方式即CSS3的meida queries(代码量比较大,维护不方便);
(2)flex 弹性布局(高度定死,宽度自适应,元素都采用px做单位。);
(3)rem+viewport缩放:根据屏幕宽度设定 rem 值,需要适配的元素都使用 rem 为单位,不需要适配的元素还是使用 px 为单位。
(4)rem 方式:随着屏幕宽度变化,页面也会跟着变化,效果就和PC页面的流体布局差不多,在哪个宽度需要调整的时候使用响应式布局调调就行了。
具体方法一:强制meta viewport的宽度为设计稿的宽度
 把下面的代码放在头部,然后制作稿跟PC上一样的制作就行:
!function(designWidth){
if (/Android(?:\s+|\/)(\d+\.\d+)?/.test(navigator.userAgent)) {
var version = parseFloat(RegExp.$1);
if (version > 2.3) {
var phoneScale = parseInt(window.screen.width) / designWidth;
document.write('<meta name="viewport" content="width=' + designWidth + ',minimum-scale=' + phoneScale + ',maximum-scale=' + phoneScale + ', target-densitydpi=device-dpi">');
} else {
document.write('<meta name="viewport" content="width=' + designWidth + ',target-densitydpi=device-dpi">');
}
} else {
document.write('<meta name="viewport" content="width=' + designWidth + ',user-scalable=no,target-densitydpi=device-dpi,minimal-ui,viewport-fit=cover">');
}
}(640);
见demo1
方法二
;(function(designWidth, maxWidth) {
var doc = document,
win = window,
docEl = doc.documentElement,
remStyle = document.createElement("style"),
tid;
function refreshRem() {
var width = docEl.getBoundingClientRect().width;
maxWidth = maxWidth || 540;
width>maxWidth && (width=maxWidth);
var rem = width * 100 / designWidth;
remStyle.innerHTML = 'html{font-size:' + rem + 'px;}';
}
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(remStyle);
} else {
var wrap = doc.createElement("div");
wrap.appendChild(remStyle);
doc.write(wrap.innerHTML);
wrap = null;
}
refreshRem();
win.addEventListener("resize", function() {
clearTimeout(tid); 
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener("pageshow", function(e) {
if (e.persisted) { 
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
if (doc.readyState === "complete") {
doc.body.style.fontSize = "16px";
} else {
doc.addEventListener("DOMContentLoaded", function(e) {
doc.body.style.fontSize = "16px";
}, false);
}
})(1920, 1920);
使用方法:
1.以上代码加入页面最前面;
2.按照设计稿尺寸填写最后两个参数:第一个参数是设计稿的宽度,第二个参数页面的最大宽度,如1920 1920。
3.使用1rem=100px转换你的设计稿的像素,例如设计稿上100px,换算成rem则为1rem。
见demo2
 

小小豆芽_

赞同来自:

精确计算出不同屏幕所应有的rem基准值:var dpr, rem, scale;
var docEl = document.documentElement;
var fontEl = document.createElement('style');
var metaEl = document.querySelector('meta[name="viewport"]');
scale = 1 / dpr;
dpr = window.devicePixelRatio || 1;
rem = docEl.clientWidth * dpr / 10;
// 设置viewport,进行缩放,达到高清效果
metaEl.setAttribute('content', 'width=' + dpr * docEl.clientWidth + ',
                     initial-scale=' + scale + ',maximum-scale=' + scale + ',
                     minimum-scale=' + scale + ',user-scalable=no');
// 设置data-dpr属性,留作的css hack之用
docEl.setAttribute('data-dpr', dpr);
// 动态写入样式
docEl.firstElementChild.appendChild(fontEl);
fontEl.innerHTML = 'html{font-size:' + rem + 'px!important;}';
// 给js调用的,某一dpr下rem和px之间的转换函数
window.rem2px = function(v) {
    v = parseFloat(v);
    return v * rem;
};
window.px2rem: function(v) {
    v = parseFloat(v);
    return v / rem;
};
window.dpr = dpr;
window.rem = rem;
参考链接:
https://www.cnblogs.com/ranyonsue/p/6795943.html
https://www.cnblogs.com/MaggieGao/p/6994868.html

爱叫啥叫啥

赞同来自:

一、meta viewport(视口)
为了解决页面缩放的体验问题,在网页代码的头部,加入一行viewport元标签。
<meta name="viewport" content="width=device-width, initial-scale=1">​这里的device-width告诉浏览器,将视口的宽度设置为设备宽度(这个宽度是人为预设的,不设的话就是980px)。
属性含义
    initial-scale:第一次进入页面的初始比例
    minimum-scale:允许缩小最小比例
    maximum-scale:允许放大最大比例
user-scalable:允许使用者缩放,1 or 0 (yes or no)
二、图片适配
img { max-width: 100%; }
此时图片会自动缩放,同时​图片最大显示为其自身的100%(即最大只可以显示为自身那么大)
为什么不用 img { width: 100%; }
当容器大于图片宽度时,图片会无情的拉伸变形
三、媒体查询
针对不用的设备提前为网页设定各种 CSS 样式CSS3中的media query模块,自动检测屏幕宽度,然后加载相应的CSS文件​
@media screen and (min-width:1200px){
        body{        
            background-color: red; 
        }​
当屏幕宽度大于1200px时,背景色变为红色​
优点
media query可以做到设备像素比的判断,方法简单,成本低,特别是对移动和PC维护同一套代码的时候。目前像Bootstrap等框架使用这种方式布局
图片便于修改,只需修改css文件
调整屏幕宽度的时候不用刷新页面即可响应式展示
缺点
代码量比较大,维护不方便
为了兼顾大屏幕或高清设备,会造成其他设备资源浪费,特别是加载图片资源
为了兼顾移动端和PC端各自响应式的展示效果,难免会损失各自特有的交互方式
四、动态 rem 方案
和媒体查询配合,实现响应式布局
px、em、rem 有什么不同?
px是pixel(像素),是屏幕上显示数据的最基本的点,在HTML中,默认的单位就是px;em 是一个相对大小,相对于父元素font-size的百分比大小;
rem 是相对于根元素<html>的font-size
用法示例
1、首先在HTML中设置一个基准值,如font-size: 30px;
2、将像素值除以30(设定的基准值)计算出对应的rem的值
如果一个div的宽度为60px ,改为rem即为2rem
3、根据当前屏幕的宽度和设计稿的宽度来计算此时的HTML的font-size的值
当前手机屏幕宽度为400px,设计稿宽度为480px,400/480*30=fontsize=25
五、Flex弹性布局 
以天猫的实现方式进行说明:它的viewport是固定的<metaname="viewport"content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
高度定死,宽度自适应,元素都采用px做单位。
随着屏幕宽度变化,页面也会跟着变化,效果就和PC页面的流体布局差不多,在哪个宽度需要调整的时候使用响应式布局调调就行(比如网易新闻),这样就实现了适配。
六、rem + viewport 缩放 
这也是淘宝使用的方案,根据屏幕宽度设定 rem 值,需要适配的元素都使用 rem 为单位,不需要适配的元素还是使用 px 为单位。(1em = 16px)
实现原理 
根据rem将页面放大dpr倍, 然后viewport设置为1/dpr.如iphone6 plus的dpr为3, 则页面整体放大3倍, 1px(css单位)在plus下默认为3px(物理像素) 。然后viewport设置为1/3, 这样页面整体缩回原始大小. 从而实现高清。 
这样整个网页在设备内显示时的页面宽度就会等于设备逻辑像素大小,也就是device-width。 
这个device-width的计算公式为:设备的物理分辨率/(devicePixelRatio * scale), 
在scale为1的情况下,device-width = 设备的物理分辨率/devicePixelRatio 。

半糖

赞同来自:

百分比适配 
就是按照设计稿,根据元素的大小和位置进行百分比的换算,例如480px的设计稿,里边有图片宽度是240px,就需要在css里设置最外层的框设置宽为100%,图片的父级设置50%的宽度,图片设置100%的宽度,高度设置auto,这样在不同的手机上图片始终是屏幕的50%,理论是没有错,因为图片放在元素里时,会自己占位,高度会按自己的宽来计算,但是放在背景图片里,就需要给高度,就会出现拉伸的情况,影响用户体验,虽然给background-image设置一系列相关属性background-size、background-position和background-repeat后,图片不会形变,但是在页面展示环境没有约束的情况下,就会出现一屏幕显示不完的问题。之外,部分图片用百分比显示会降低分辨率,变模糊;部分设计稿里边有1px边框,如果边框也按百分比写,在部分小设备里会消失,如果直接按物理像素写,在小设备里可能会出现排版乱。
媒体查询media
css3的媒体查询,可以按照自己需要的不同的宽度来写不同宽度的样式,不担心会影响其他宽度里的布局,就像是一块地盘内只有一个老大,大家出来混的都听他的,而且他只在给定的宽度里代码才会有效,使用media来指定适配的媒体介质,使用screen较多,比如
@media screen and (min-width:320px) and (max-width:399px) {.box{width:30%;}}
就是在屏幕为320px到399px的设备下,class为box的盒子宽度为30%,据我到处浏览,目前使用media的是最多,不用考虑元素会变拉伸变型
但是使用媒体查询在css中会需要增加很多的不同分辨率的样式代码,可能会觉得有很多冗余,但是毫无疑问,媒体查询离我们理想中的适配方案已经很近了。
viewport
通过对mata中viewport的scale设置,也可以实现适配,如下
<meta name="viewport" content="width=device-width, initial-scale=1.0,
maximum-scale=1.0,minimum-scale=1.0,user-scalable=0" />
这种方法说白了就是把你的设计稿和手机屏幕分辨率做一个比例缩放,连文字大小也会根据比例缩放。
initial-scale:设置页面的初始缩放值,为一个数字,可以带小数
minimum-scale:允许用户的最小缩放值,为一个数字,可以带小数
maximum-scale:允许用户的最大缩放值,为一个数字,可以带小数
height:设置layout viewport 的高度,这个属性对我们并不重要,很少使用
user-scalable:是否允许用户进行缩放,值为”no”或”yes”, no 代表不允许,yes代表允许
<script>
var deviceWidth = parseInt(window.screen.width); //获取当前设备的屏幕宽度
var deviceScale = deviceWidth / 480; //得到当前设备屏幕与480px之间的比例,之后我们就可以将网页宽度固定
为480px
var ua = navigator.userAgent;
//获取当前设备类型(安卓或苹果)
if (/Android (\d+\.\d+)/.test(ua)) {
var version = parseFloat(RegExp.$1);
if (version > 2.3) {
document.write('<meta name="viewport" content="width=480,initial-scale=' + deviceScale + ',
minimum-scale = ' + deviceScale + ', maximum-scale = ' + deviceScale + ', target-densitydpi=device-dpi">');
} else {
document.write('<meta name="viewport" content="width=480,initial-scale=0.75,maximum-scale=0.75,
minimum-scale=0.75,target-densitydpi=device-dpi" />');
}
} else {
document.write('<meta name="viewport" content="width=480, user-scalable=no">');
}640
</script>
对于小设备,会把字体也相对缩小,这样一些注释啦什么的就会太小了。
rem
rem代表的是根元素字体大小,多数主流浏览器都默认font-size是16px,这也就不需要担心rem使用中的兼容问题,
先需要在html里设置跟字体的大小,如
 html{font-size:62.5%;}
这样就把默认的根字体大小设为10px了,62.5%=10/16*100%,设置好之后就可以用rem来设定元素属性了,由于计算麻烦,编辑器也开发了各种插件,可以弹出提示
也可以限制在多大屏幕下,rem是多大,这里以480px屏幕下为例  1rem=30px
var html = document.getElementsByTagName("html")[0];
var rootResize = function () {
var winClient = document.documentElement.clientWidth;
var fontSize = winClient < 480 ? winClient / 16 : 30;
if (fontSize < 20) {
fontSize = 20;
}
html.style.fontSize = fontSize + "px";
}
rootResize();
window.onresize = function () {
rootResize();
}
这些方法都可以配合着用,比如先在meta里加viewport  scale,然后图片用百分比,动态设置不同屏幕宽度下rem的值,结合文字流式,控件弹性,图片等比缩放,最后再用媒体查询微调一下

ry

赞同来自:

1.固定高度,宽度自适应
使用了理想视口:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
垂直方向使用固定的值,水平方
向使用弹性布局,元素采用定值、百分比、flex布局等。
这种方案相对简单,还原度也非常低。

2.根据不同屏幕动态写入font-size,以rem作为宽度单位,固定布局视口。
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=0,minimal-ui,viewport-fit=cover">
如:荔枝FM
(function(e) {
var n=750,t=100,i=e.document.createElement("div");i.style.width="1rem",i.style.display="none";var d=e.document.getElementsByTagName("head")[0];d.appendChild(i);var r=parseFloat(e.getComputedStyle(i,null).getPropertyValue("width"));i.remove(),document.documentElement.style.fontSize=e.innerWidth/n*t/r*100+"%";var a=document.createElement("style"),m="@media screen and (min-width: "+e.innerWidth+"px) {html{font-size:"+e.innerWidth/(n/t)/r*100+"%;}}",l="@media screen and (min-width: "+e.innerHeight+"px) {html{font-size:"+e.innerHeight/(n/t)/r*100+"%;}}";return a.innerHTML=m+l,d.appendChild(a),r
})(window);
如:小米官网
!function(e) {
var t = e.document,
n = t.documentElement,
i = e.devicePixelRatio || 1,
a = "orientationchange" in e ? "orientationchange" : "resize",
d = function() {
var e = n.getBoundingClientRect().width || 360;
(1 == i || 720 < e) && (e = 720), n.style.fontSize = e / 7.2 + "px"
};
n.setAttribute("data-dpr", i), t.addEventListener && (e.addEventListener(a, d, !1), "complete" === t.readyState || t.addEventListener("DOMContentLoaded", function() {
setTimeout(d)
}, !1))
}(window)
故对于设计稿上任何一个尺寸换成rem后,在任何屏下对应的尺寸占屏幕宽度的百分比相同。但这个有兼容性问题,谷歌浏览器能识别的最小字体为12px, 无法再小了

3.用rem做单位,通过css3媒体查询来改变html中的font-size值。
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=0,minimal-ui,viewport-fit=cover">
vm/vh:CSS单位
如网易新闻
html{font-size:13.33333vw}
@media screen and (max-width:320px) {
html {
font-size: 42.667px;
font-size: 13.33333vw
}
}
@media screen and (min-width:321px) and (max-width:360px) {
html {
font-size: 48px;
font-size: 13.33333vw
}
}
@media screen and (min-width:361px) and (max-width:375px) {
html {
font-size: 50px;
font-size: 13.33333vw
}
}
@media screen and (min-width:376px) and (max-width:393px) {
html {
font-size: 52.4px;
font-size: 13.33333vw
}
}
@media screen and (min-width:394px) and (max-width:412px) {
html {
font-size: 54.93px;
font-size: 13.33333vw
}
}
@media screen and (min-width:413px) and (max-width:414px) {
html {
font-size: 55.2px;
font-size: 13.33333vw
}
}
@media screen and (min-width:415px) and (max-width:480px) {
html {
font-size: 64px;
font-size: 13.33333vw
}
}
@media screen and (min-width:481px) and (max-width:540px) {
html {
font-size: 72px;
font-size: 13.33333vw
}
}
@media screen and (min-width:541px) and (max-width:640px) {
html {
font-size: 85.33px;
font-size: 13.33333vw
}
}
@media screen and (min-width:641px) and (max-width:720px) {
html {
font-size: 96px;
font-size: 13.33333vw
}
}
@media screen and (min-width:721px) and (max-width:768px) {
html {
font-size: 102.4px;
font-size: 13.33333vw
}
}
@media screen and (min-width:769px) {
html {
font-size: 102.4px;
font-size: 13.33333vw
}
}
如:京东
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no, viewport-fit=cover">
html {
font-size: 20px;
font-size: 5.33333vw
}
@media screen and (max-width:320px) {
html {
font-size: 17.06667px
}
}
@media screen and (min-width:540px) {
html {
font-size: 28.8px
}
}

要回复问题请先登录注册