首先要知道,在移动开发中,手机的浏览器会默认网页是为宽屏而设计的,它会缩小整个页面来适应屏幕。
1. 不使用viewport出现的问题
提到响应式设计,大家首先想到的可能是 Bootstrap , @media 。
@media(媒体查询)是CSS3的属性,利用它可以在不依赖其他库的情况下实现响应式设计。示例代码如下:
@media screen and (min- 960px) { body { background-color: blue; } } @media screen and (max- 960px) and (min- 500px) { body { background-color: red; } } @media screen and (max- 500px) { body { background-color: yellow; } }
我在body里加了一段话,在PC端效果如下:
开始:
缩小浏览器窗口:
再缩小浏览器窗口:
我把这样页面放到服务器上,用我的手机访问,我的手机屏幕宽度是414px。那么这个页面在我手机上应该是黄色。请看屏幕截图:
是蓝色的。那么问题来了,为什么媒体查询失效了呢。这是由于设备尺寸和Viewport尺寸不一致导致的。为了解释这个问题,首先我们要理解移动设备上的1px != css中的1px。
2. css中的px与移动设备的px
在css中我们一般使用px作为单位,在桌面浏览器中css的1个像素往往都是对应着电脑屏幕的1个物理像素,这可能会造成我们的一个错觉,那就是css中的像素就是设备的物理像素。但实际情况却并非如此,css中的像素只是一个抽象的单位,在不同的设备或不同的环境中,css中的1px所代表的设备物理像素是不同的。影响css中px与设备px换算的因素主要有以下两个:
- 设备尺寸
- 用户缩放
设备尺寸
iphone3的分辨率为320x480,在iphone3上,一个css像素确实是等于一个屏幕物理像素的。从iphone4分辨率提高了一倍,变成640x960,但屏幕尺寸却没变化,这就意味着同样大小的屏幕上,像素却多了一倍,这时,一个css像素是等于两个物理像素的。其他设备类似。
用户缩放
当用户把页面放大一倍,那么css中1px所代表的物理像素也会增加一倍;反之把页面缩小一倍,css中1px所代表的物理像素也会减少一倍。你可以想象,css中的px是一个一个小格子,在缩放页面的时候,其实是在缩放每个小格子,而设备物理像素显然是大小不变的。
在移动端浏览器中以及某些桌面浏览器中,window对象有一个devicePixelRatio属性,它的官方的定义为:设备物理像素和设备独立像素的比例,也就是 devicePixelRatio = 物理像素 / 独立像素。css中的px就可以看做是设备的独立像素,所以通过devicePixelRatio,我们可以知道该设备上一个css像素代表多少个物理像素。在上面例子中增加js脚本:
window.alert(window.devicePixelRatio);
在PC端:
在我的手机上:
设备像素比是不同的。
3. 三种视口(viewport)
你可能以为我啰嗦这么多终于要进入正题了。咳咳,其实我还是想让你了解下面三个概念。如果你熟悉移动端开发,请跳过这节直接看4 ,如果你刚刚接触移动端开发,我建议你读下去,早晚你得了解。
3.1 布局视口(layout viewport)
移动设备上的浏览器认为自己必须能让所有的网站都正常显示,即使是那些不是为移动设备设计的网站。但如果以浏览器的可视区域作为viewport的话,因为移动设备的屏幕都不是很宽,所以那些为桌面浏览器设计的网站放到移动设备上显示时,必然会因为移动设备的viewport太窄,而挤作一团,甚至布局什么的都会乱掉。所以这些浏览器就决定默认情况下把viewport设为一个较宽的值,比如980px,这样的话即使是那些为桌面设计的网站也能在移动浏览器上正常显示了。这个宽度被称为布局视口(layout viewport),可以通过document.documentElement.clientWidth
获得。
3.2 视觉视口(visual viewport)
虽然独立布局视口的创造很大程度地帮助了桌面网站到手机上的转移,但我们不能完全无视移动端设备的屏幕尺寸。一些CSS声明与用户见到的东西有关,而与CSS的初始包含块无关。并且,有时候知道用户看到了网站的哪些部分对web开发者会有帮助。视觉视口是用户正在看到的网站的区域,对于的javascript属性是window.innerWidth
.
缩放会影响视觉视口的大小。当缩放程度是100%时,视觉视口与设备屏幕一样宽。放大使视觉视口变得更小,因为屏幕上显示的CSS像素更小了,而缩小会让视觉视口更大,因为屏幕上的CSS像素更多了。因此缩放程度和视觉视口的大小是逆相关的:放得越大,视觉视口越小。
注意:当用户缩放时,只有视觉视口的尺寸会发生改变,布局视口不会改变。移动端的缩放不会导致CSS布局被重新计算。
3.3 理想视口(ideal viewport)
现在越来越多的网站都会为移动设备进行单独的设计,所以必须还要有一个能完美适配移动设备的viewport。所谓的完美适配指的是,首先不需要用户缩放和横向滚动条就能正常的查看网站的所有内容;第二,显示的文字的大小是合适,比如一段14px大小的文字,不会因为在一个高密度像素的屏幕里显示得太小而无法看清,理想的情况是这段14px的文字无论是在何种密度屏幕,何种分辨率下,显示出来的大小都是差不多的。当然,不只是文字,其他元素像图片什么的也是这个道理。这个视口被称为理想视口(ideal viewport),这个尺寸是厂商确定的。
只有当网站是为手机准备的时候才应该使用理想视口。只有主动地往页面里添加meta视口标签时理想视口才会生效。如果没有meta视口标签声明,那么布局视口将会维持它的默认宽度,理想视口只有当显式地使用它的时候才会产生影响:
//这一行代码告诉浏览器,布局视口的宽度应该与理想视口的宽度一致 <meta name="viewport" content="width=device-width">
3.4 测试三种视口
在head标签中加入<meta name="viewport" content="width=device-width">
,使布局视口宽度等于理想视口宽度,加入下面js代码进行测试。
var ratio = window.devicePixelRatio; var layoutWidth = document.documentElement.clientWidth; var visualWidth = window.innerWidth; window.alert('devicePixelRatio: ' + ratio + ' || layout viewport: ' + layoutWidth + ' || visual viewport: ' + visualWidth);
PC端:
手机:
4 使用meta标签控制viewport实现响应式设计
移动设备默认的viewport是layout viewport,也就是那个比屏幕要宽的viewport,但在进行移动设备网站的开发时,我们需要的是ideal viewport。那么怎么才能得到ideal viewport呢?这就该轮到meta标签出场了。
4.1 设置meta标签
我们在开发移动设备的网站时,最常见的的一个动作就是把下面这个东西复制到我们的head标签中:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
该meta标签的作用是让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。meta标签中共有6个关键字:
- width:可以用width来设置viewport的宽度,以替代那些不合适的默认宽度。我们可以给其设定一个固定大小,但设定成device-width更加明智一些,这样我们可以兼容不同大小的屏幕。
- height:同width
- initial-scale:控制默认的缩放比例,缩放是相对于ideal viewport来缩放的
- minimum-scale:这个是用户可以缩放页面的最大比例
- maximum-scale:这个是用户可以缩放页面的最小比例
- user-scalable:如果设置了user-scalable=no,用户将无法对页面进行缩放
了解这些以后,我们在测试例子中的head标签中加入viewport,使理想视口宽度等于布局视口宽度:
<meta name="viewport" content="width=device-width">
PC端网页效果不变,手机端访问效果:
这是我们想要的效果了。
4.2 动态改变meta标签
第一种方法
可以使用document.write来动态输出meta viewport标签,例如:
document.write('<meta name="viewport" content="width=device-width,initial-scale=1">')
第二种方法
通过setAttribute来改变
<meta id="testViewport" name="viewport" content="width = 380"> <script> var mvp = document.getElementById('testViewport'); mvp.setAttribute('content','width=480'); </script>
参考:https://blog.csdn.net/qiqingjin/article/details/51539644