这篇文章主要讨论了移动端页面渲染与布局的一些基本概念例如 viewport
和 css 像素
, 这些内容是了解移动端页面适配的基本前提.javascript
在这篇文章中记录了这些基本的概念, 由于我并不从事移动端开发, 文章有误, 欢迎指正.css
为了简单起见, 咱们不从概念讲起, 咱们先来看看一个固定宽度的元素被放置到移动端浏览器中会发生什么.html
有以下代码:java
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <style> .example{ width: 900px; background-color: aqua; } </style> </head> <body> <div class="example"> 个人宽度是 900px </div> </body> </html>
固然桌面浏览器渲染效果确定在咱们意料之中啦✨:android
图片: 1920px下chrome渲染的效果:web
而下面展现了图片展现了几个移动端浏览器渲染的结果:chrome
图片: 移动端浏览器渲染效果:windows
这里咱们简单的介绍一下图片中浏览器的版本, 这些浏览器在后文的截图中版本都是不会发生变化的.浏览器
浏览器 | 版本 | 平台 |
---|---|---|
chrome | 75 | windows10 |
chrome | 75 | android9 |
edge | 42 | android9 |
Samsung | 9.3 | android9 |
仔细观察咱们发现:900px 宽的 div 没有占满屏幕, 也就是说屏幕的宽度比 900px 还要大.
那么屏幕宽度究竟是多大呢, 咱们插入一行代码来获取HTML元素的宽度:iphone
<script> document.write(`html元素的宽度是${document.documentElement.clientWidth}`); </script>
不一样浏览器的显示结果为:
他们都输出了一样的数据那就是 980 在某种程度上咱们知道了页面的宽度, 原来页面是 980px 的宽度因此 900px 的div没法铺满一行.
对于移动浏览器它感知到本身的视口的宽度是 980px, 此时它渲染的比例就和一个 横向分辨率为 980px 的桌面显示器渲染的同样. 只不过移动终端的屏幕是一个小号的显示器而已.
在没有更改页面的设置的状况下或者说默认的状况下, 移动终端的浏览器会按照 980px 的宽度来渲染页面, 而后再将页面缩放到适合屏幕的大小.
不理解不要紧, 咱们使用一张图来理解这一行为:
为何会有这种行为?
缘由很简单, 在移动设备刚刚兴起的时候大多数的页面都是为桌面设计的在当时一个页面的内容区域的宽度通常在 980px 之内.
可是移动设备屏幕的分辨率很小, 例如某款手机的屏幕的横向分辨率是 320px 彻底不足以容纳为桌面设计的页面.
和如今的状况相反, 手机厂商得设计一种方式来兼容现有的页面确保页面在移动端布局正常, 因此手机浏览器的默认内容区域的宽度被设置为桌面页面内容区域的典型值 980px(不一样设备不一样,后面有统一方法), 为了浏览效果和桌面一致将内容区域平铺到屏幕上.
这样确保了布局正常且手机屏幕上浏览的总体效果和在桌面上的一致, 不过说到底移动端的横向分辨率只有 320px 天然有些内容看不清楚这个时候怎么办呢 -> 双击缩放呗🤣.
回想一下移动端浏览器的两个默认的渲染行为:
对于响应式设计来讲, 这种默认的行为有些问题. 例如第一种行为会致使媒体查询没没法使用, 由于移动端页面宽度固定 980px.
第二种自动缩放的行为会致使页面模糊(高分辨率的页面缩放到低分辨率的终端上), 或者难以辨别内容(高分辨率的终端致使文字过小).
在无额外的配置影响下, 下面的例子中的媒体查询不会起做用, 由于页面宽度为 980px 不会变化:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <style> .example { width: 900px; background-color: aqua; } @media screen and (max-width:960px) { .example { background-color: red; } } </style> </head> <body> <div class="example"> 个人宽度是 900px </div> <script> document.write(`html元素的宽度是${document.documentElement.clientWidth}`); </script> </body> </html>
因此一个天然的想法就产生了页面的大小应该和屏幕大小一致才对啊, 这样咱们就能够根据不一样的屏幕尺寸来定制页面展现的效果.
须要借助于 meta
标签来完成, 经过设置 <meta name="viewport" content="">
来控制页面的渲染.
在大部分的IDE和文本编辑器中这行代码被放到了建立HTML文件的模板里, 因此不少人会以为眼熟:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
这里你能够基本将这行代码理解为 "调整视口宽度和设备宽度一致", 更多的讨论咱们在随后的中会提到.
图片:适应设备宽度后的渲染效果:
注意:上面图片中的三个浏览器实际上都是有横向滚动条的, 由于 900px 的 div 宽度超过了视口宽度.
页面中的内容显得更加大了一些, 除此之外看起来也没太大变化嘛. 不过这个手机看起来分辨率挺低的啊 html元素宽度竟然是 412px , 这个年头的老人机的分辨率都比这个高了吧!
是的 HTML元素宽度确实是 412px , 可是个人手机屏幕的横向分辨率倒是 1440px, 为何?
看来咱们又有了一个新的问题, 不过好在下一章中咱们就能够解开这个谜团了.
css
像素与物理像素css
中的 px
单位也就是咱们常说的 "像素" 和显示器屏幕这个物理设备上提到的 "像素" 不是同一个东西, 换句话说 "css
中的像素" 不等于 "物理像素", 物理像素是屏幕上实际的像素点, 而 css 像素
是一个虚拟的单位.
这有点违反直觉, 由于在桌面浏览器上咱们设置的一个 1px 大小的元素, 他所占的实际大小也是一个像素.
这由于大部分状况下桌面操做系统中设定的屏幕分辨率和显示器的物理分辨率都是一致的, 因此就会形成 "css 中的像素" 和 屏幕上的像素是同一个概念的误会.
不过证实 "css
中的像素" 不等于 "屏幕像素" 也不难, 例如在 windows 10
中你能够在 显示设置->缩放与布局
中设置系统缩放比例为 125%
. 此时一个横向分辨率为 1920
的显示器去输出 html 元素的宽度的时候是 1536px.
图片: 缩放选项:
图片:chrome和firefox的 HTML 元素的宽度:
另外你也能够简单的经过 ctrl + 滚轮上/下
来调整浏览器的缩放来模拟这种状况. 不过你始终要记住这无非是使用了多个物理像素来显示一个 css 像素
而已.
注意: 修改系统缩放比例会致使 window.screen
读取到的数据也是缩放后的而不是屏幕的原始分辨率.
在早期的移动设备中常见的横向物理分辨率是 320px. 可是在现代的移动设备中的屏幕物理分辨率大部分都超过了 320px, 实际上现现在桌面显示器还在 1080p 的阶段, 移动设备的分辨率大部分都接近 2k 了, 因此这里会出现一个问题, 移动设备的屏幕过小若是一个css
像素等于一个物理像素那么显示的东西会挤在一团致使没法浏览. 因此终端厂商会在设备里面内置了一个 "最佳显示分辨率" 用于优化显示.
毫无疑问大部分的终端中这个 "最佳显示分辨率" 都小于设备的 "物理分辨率" , 由于它们使用多个 "物理像素" 来显示一个 "css像素".
例如:咱们有一个设备的横向物理像素数量是 1920, 某个终端制造商设置的内置的最佳显示值是 960, 那么咱们就是用一个css
像素来对应两个物理像素, 效果就是把显示的内容放大了一倍, 那么这个比值就是 1920 / 960 = 2 ,而这个值就是常说的设备像素比, 在浏览器中这个值咱们可使用 windows.devicePixelRatio
来获取.
我在 windows 10
系统缩放 125% 状况下和移动端中执行以下代码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .example { width: 900px; background-color: aqua; } </style> </head> <body> <div class="example"> 个人宽度是 900px </div> <script> document.write(`html元素的宽度是${document.documentElement.clientWidth}px`); document.write(`<br>`); document.write(`设备像素比是${window.devicePixelRatio}`); document.write(`<br>`); document.write(`实际的物理分辨率是 ${screen.width} * ${window.devicePixelRatio} = ${screen.width * window.devicePixelRatio}px`); </script> </body> </html>
windows 10
输出结果以下:
一个高分辨率的手机会有一个更大的设备像素比, 这样文字才可以大才能看的清楚:
viewport(视口) 的概念很是简单, 至关于浏览器整块渲染区域, 或者说它等于浏览器窗口, viewport 拥有它的宽高.
在一个空白的页面中存在一个 div 这个 div 被设置了样式:
div { width:30%; height:500px; }
他的宽度被设定了一个相对值, 当窗口宽度发生改变的时候, 他会随着改变宽度. 那么这个百分比值相对的是谁呢? 根据规范是相对于父元素, body
的父元素是 html
而 html
的父元素就是 viewport
.
移动端的状况比较复杂一些, 这里有两个基本概念 visual viewport
和 layout viewport
.
如今的移动端页面都是通过适配的, 因此要想找到一个早先的移动页面来举例比较困难, 不过请请你试想之前你用手机浏览那些没有作移动端适配的页面所发生的状况.
这种状况就是, "页面上的内容很是小, 你须要经过双指放大才能够看清页面上的细节, 此时的页面每每会有横向滚动条", 例以下方这张中国银行电脑版在移动端的截图:
这种行为就好像透过望远镜观察星空同样, 星空的大小是固定的, 可是咱们能够透过望远镜选择放大来观察局部的细节仍是缩小来观察总体.
上面的例子中 "星空" 就是咱们的页面, 而望远镜就是咱们的手机屏幕, 星空表明的就是 layout viewport
也就是咱们的页面, 而望远镜表明的就是 visual viewport
也就是咱们的页面的可视区域.
星空有固定的大小, 那么 layout viewport
固定的大小是多少, 这个取决于浏览器, 不过如今常见的大小是 980px, 这个值你能够在本文稍早的移动端截图中找到.
你在页面上设置的 css 像素
都会做用在 layout viewport
上, 而不是 visual viewport
上.
layout viewport
layout viewport
能够经过 document.body.clientWidth/Height
来进行获取, 本质上咱们没法直接获取 layout viewport
的大小, 可是在默认状况下 HTML 元素的大小就是 layout viewport
的大小, 由于块级元素继承了 viewport
的 100% 的宽度.
就像以前提到的同样 layout viewport
的值在未经设置的状况下是固定的. 不管设备是竖屏仍是横屏这个宽度都是 980px(默认状况下).
图片: 竖屏效果:
图片: 横屏效果:
能够看到 document.body.clientWidth/Height
的值(图片标红的位置) 并未发生变化.
visual viewport
visual viewport
能够经过 window.innerWidth/Height
来进行衡量. 这个属性会以 css 像素
做为单位返回值, 告诉你页面中还有多少空间能够用于布局, 很显然当页面放大这个值会减小, 页面缩小这个值会变大.
一个更加明显的例子就是, 当移动端键盘弹起的时候 visual viewport
会发生改变, 而 layout viewport
是不会变化的, 经过这个差别咱们能够判断键盘是否弹出.
注意: window.innerWidth/Height
这个值会包含滚动条的大小.
注意: 这里介绍的 VisualViewport
仍是一项实验中功能.
Visual Viewport API
的 VisualViewport
接口表明了给定窗口的 visual viewport
.
这个对象接口是一个全局属性, 它提供了两个事件:
使用示例:
window.visualViewport.addEventListener('resize', resizeHandler);
另外他还提供了一些有用的属性, 这些属性都是只读的:
VisualViewport.offsetLeft
返回 visual viewport
相对于 layout viewport
左边缘的偏移量. (css 像素)
VisualViewport.offsetTop
返回 visual viewport
相对于 layout viewport
顶边缘的偏移量. (css 像素)
VisualViewport.pageLeft
返回 visual viewport
顶边缘相对于初始包含块原点的x坐标.(css 像素)
VisualViewport.pageTop
返回 visual viewport
顶边缘相对于初始包含块原点的y坐标.(css 像素)
VisualViewport.width
返回 visual viewport
的宽度. (css 像素)
VisualViewport.height
返回 visual viewport
的高度.(css 像素)
VisualViewport.scale
返回应用到 visual viewport
上的缩放因子.
<meta name="viewport">
<meta name="viewport">
属性在乎义在于控制 layout viewport
的宽度(由于 web 开发不关心高度). 你能够为其指定 css 像素
来获取一个固定的宽度.
可是在大部分状况下咱们但愿 layout viewport
是跟随设备的大小而改变的, 下面咱们经过修改其 width
属性来控制它的大小:
示例1, 设置为 500px:
<meta name="viewport" content="width=500px">
示例2, 等于屏幕的物理像素:
<meta name="viewport" content="width=device-width">
注意: 就像我以前在 css 像素与物理像素 这节提到的, 这个属性最终获得的值并不会是实际的物理像素, 而是由设备厂商提供的一个建议值.
除了能够指定 width
咱们还能够指定其余的属性, 这些属性经过逗号隔开, 例如使用 user-scalable=no
来禁止用户缩放页面, 使页面看起来像原生应用:
<meta name="viewport" content="width=device-width, user-scalable=no">
下方的表格中提供了 viewport
的几个可选属性:
属性 | 描述 |
---|---|
width | 设置layout viewport 的宽度,为一个正整数,或字符串"width-device" |
initial-scale | 设置页面的初始缩放值,为一个数字,能够带小数 |
minimum-scale | 容许用户的最小缩放值,为一个数字,能够带小数 |
maximum-scale | 容许用户的最大缩放值,为一个数字,能够带小数 |
height | 设置layout viewport 的高度,这个属性对咱们并不重要,不多使用 |
user-scalable | 是否容许用户进行缩放,值为"no"或"yes", no 表明不容许,yes表明容许 |
和 width=device-width
功能同样的还有 initial-scale=1
, 有时咱们会经常看到这两个属性是被同时设置的:
<meta name="viewport" content="width=device-width, initial-scale=1">
经过设置 initial-scale=1
是告诉浏览器相对于 ideal viewport
(这个概念来自于 Meta viewport 一文, 我把它理解设备厂商内置的最佳显示分辨率也就是 device-witdh
的大小) 进行缩放且缩放一倍, 因此他会和 width=device-width
效果一致.
一样根据文中所述, 这种写法是用来解决兼容问题, 由于 IE 不支持 initial-scale=1
而 iphone 和 ipad 不支持 width=device-width
.
当这两个属性同时使用的时候:
<meta name="viewport" content="width=400, initial-scale=1">
浏览器会取它们两个中较大的那个值, 若是 ideal viewport
是 500px 那么浏览器就会使用 500px, 反之使用 400px.
在后续我会介绍几种常见的移动端适配方案, 和它们的工做原理, 因为工做关系可能须要一些时间才会写完.
十分建议读者阅读下列的几篇文章, 它们一样也是本文章的重要参考来源之一, 原文是英文的, 不过已经被他人翻译完成, 可是因为原连接已经丢失, 这里提供转载的地址, 原文一共有三篇, 这里按顺序给出:
https://www.jianshu.com/p/738...