前言
关于手机屏幕响应式编程,从大约从iphone 6时代就开始讨论了,之后的方案是一年一个,如今,都2019年了,也该有个结论了。
以往曾经流行过em方案,后来是rem方案,后来又有vw方案,到最近两年,由于脚手架的逐步进化,加上vue等框架的流行,尺寸现在都流行动态计算,也就是说:
将屏幕宽度假想为750个单位,单位可以叫“rpx”(微信小程序),uni-app里叫“upx”,不过也不要以为uni-app的upx只是给rpx改了改名,毕竟在H5端和APP都是生效的。如果看uni-app生成的H5,会发现,尺寸单位依然是px,显然,所有px值都是由js计算出来的(并不是脚手架生成的静态值)。
这就好玩了,这种方式打脸了一些使用vw、rem的技术方案,因为我就px一万年,依然做得到响应式、自适应,但是它的技术细节就不太容易弄清。
而今天我讲的viewport元数据方案,是最简单,但又没什么明显缺陷的方案。当然,比起uni-app还是差一点,毕竟技术摆在那。
我个人做法是优先用uni-app,简单的H5就用viewport方案,能应付百分之九十九的场景。
viewport元数据方案科普
提起viewport元数据,最典型的元数据规则是:
实际书写的时候应该写成1行,我这里为了方便阅读给做了换行。
这几句是什么意思?可以看我写的《通俗讲解CSS pixels、device pixels、device-width、layout viewport、visual viewport、ideal viewport六个概念》。
好,现在假定你阅读了上文。说白了,device-width
就是设备逻辑像素宽度,在最早期就等于320,后面手机越出越多,device-width
可能等于360、375、414等等,手机出厂之前就定好了。
viewport元数据方案内容
以360px逻辑分辨率作为页面宽度标准,以16px作为标准字体尺寸。
设计师的画布可以为720px,也可以为750px,也可以为1080px,都成,越大则手机显示的画面越细腻。
除字体之外的尺寸,都根据360px的画布来定px值,比如一个正文页面,正文的留白就可以是12px。完全不用考虑什么rem之类的尺寸单位。
字体的尺寸,上面说了,标准就是16px,想要小字体就写14px、12px、10px,想要大字体就写18px等等。也完全不用考虑rem之类的尺寸单位。
-
加一些代码:
a. 在最顶部写上一个没有content的元数据:
b. 紧贴上文代码,写上:
viewport元数据方案原理
viewport元数据的牛逼之处就在于缩放。
首先说为什么用360px作为标准,因为360px接近真实的手机宽度,你在电脑上用QQ截图工具量出一个360px长度(别用视网膜屏幕量,这不准),然后你把自己手机贴近电脑屏幕,你会发现360px长度跟手机宽度差不多。
然后,还有一点,就是360px宽度下,用16px作为标准字体,是符合人眼的最舒适感的。如果你要用375px作为标准宽度,那么16px的字就显得比较小,不适合阅读了。当然了,这就纯看你的想法了,因为360px配16px字最佳这句话是我说的,不是公认的,你也可以认为375px配16px字最佳,这就是人眼的感受不同了,就好比有些人喜欢看小字,有些人喜欢看大字,这就不是程序上的“1+1=2”那么死板了。
viewport元数据的作用就是,先把width强行设为360。这时候,很多安卓手机本身就是360逻辑像素,所以还好,但是iPhone最新的手机都是414逻辑像素,这时候,如果不做其他设置,那么iPhone手机上显示的字都是偏小的。然后再给initial-scale
、maximum-scale
、minimum-scale
设一个比例尺,这个比例尺就是动态计算出来的,比如iPhone 6的比例尺就是375 / 360 = 1.04167,这样,浏览器会把整个页面再放大1.04167倍,就实现了各个手机一致的显示效果。
也就是说,屏幕一行永远是显示360 / 16 = 22.5个字,无论你手机多小,还是多大,都是这样的一行22.5个字。而图片呢?比如一张图设了180px宽,那么在小手机是占左半屏那么宽,在大手机上也是占左半屏那么宽。
这样就实现了响应式。
这种方式的实现,源自于浏览器级别的渲染原理,也是最准确的渲染,是最严格意义的放大缩小。
案例
浏览器打开http://common.hebei.sina.com.cn/app/2019/ajsc2019/,然后F12,切换手机模式,先看iPhone 5模式,刷新页面,整个页面是比较小的,元数据是这样:
然后切换到iPhone 6,刷新页面,页面整体变大,元数据是这样:
那么,是不是尺寸值都是用的px呢?select一个首页的图片看看:
可以看到,就是px值,而且是基于360px画布写出来的px值。
发丝线
这个方案不可解决发丝线宽度的问题,想解决发丝线宽度,还是要用发丝线本身的解决方案。
方案缺陷
不支持viewport的手机浏览器,就不能采用这种方案了,比如安卓古老版本的自带浏览器。但是,2019年了,还有这种手机么?这种手机的使用者又能给你带来多少价值呢?
-
设计稿中的px值都要经过换算才能用。怎么办?有2种办法:
设计规范里面的固定尺寸,和常用尺寸,就直接脑子记住,比如留白,设计稿里是20px,那么我们css里就写10px就可以了,尽管严格说应该写20 * 360 / 750 = 9.6px,但是也没必要那么精确了,10px并不是不可以,只要你坚持用10px就行。
不在设计规范里的尺寸,要么就简单的口算除以2,想要精确值就可以使用sass计算,我们先定义一个mixin和一个function,mixin用于多个值,fuction用于单个值,各有各的场合:
@mixin scaler($property, $values...) { $resultValues: ''; @each $value in $values { $resultValues: #{$resultValues + $value * 36 / 75 + "px "}; } #{$property}: $resultValues; } @function scaler($value) { @return #{$value * 36 / 75 + 'px'}; }
其中36和75根据你的实际情况改。
使用:
body { @include scaler(margin, 2, 4, 6, 8); padding: scaler(5) scaler(10); }
结果:
其他css预处理器也同样道理。
从这个角度说,拿375px作为标准宽度似乎更便于开发,所以尺寸都除以2就好了。总而言之,随你定。
最后一个问题,为什么标准宽度不直接用750px?
标准宽度用750px也是可以的,这样的话,尺寸就可以完全照抄设计稿(如果设计稿是750px的话)。这时候就是字体尺寸怎么写的问题了。
假设我真让标准宽度使用750px,那么字体的标准尺寸就应该是16 * 750 / 360 = 33.33px,约等于34px,作为标准字体尺寸,记下这个尺寸。或者你就用32px作为标准尺寸也是可以的,还是那句话,你们开发者和你们收到的反馈说多少px看着舒服,就用多少px。
比34px字体小一点的字体就可以是32px、30px,依然2个2个的递减。
这时候还有一个问题就是兼容PC端的问题。
上面的例子有一个 http://common.hebei.sina.com.cn/app/2019/ajsc2019/,它是兼容PC端的,在PC端上viewport失效,像素都是PC真实像素,这时候,如果标准宽度是360px,那么它在PC端依然显示360px,跟真实手机的大小是接近的,只要经过简单样式兼容,就可以兼容PC端。而如果是以750px为标准,那么就会在PC端显示一个750px宽度的网页,显然,这是一种傻大傻大的网页,体验会非常差。
所以结论是,如果你打算兼容PC端,那么标准宽度设为360还是375都可以,但不能是750px,如果从来不需要兼容PC端,那么标准宽度设750px完全可以,毕竟跟设计稿尺寸完全一致,不需要换算,是一个优势。
假设你决定了用750px作为标准宽度,那么,下面这段代码依旧必须写,只不过360要改成750而已。
那么uni-app做的H5兼容PC端么?
默认不兼容,需手动改造可以实现兼容。
如果所有的组件都是以upx为单位开发,那么只需要限定body宽度为375px,同时为了把fixed元素约束在375px内,需要给body加上transform: translateZ(0)
(原理见如何让position: fixed不再基于浏览器窗口定位?)。
如果有些尺寸使用了upx之外的单位,那就麻烦大了,兼容性比较难调,有一个办法是做一个居中的、375px宽、浏览器那么高的iframe,嵌套你的H5,也可以实现PC端兼容。
vw、rem方案兼容PC端么?
至少市面上的方案默认不去考虑PC端,也就是说PC打开页面也是浏览器全宽,页面傻大到爆炸。
所以如果要兼容也需要另外考虑,就不多说了。