第三代移动端布局方案

第三代移动端布局方案大家有没有发现淘宝的H5移动端没有使用任何rem和vw单位,而是和web端项目一样,使用的是px单位。虽然是px但它也很完美的将整个页面渲染了出来。那淘宝的FE是怎么实现的呢?最近在研究关于布局的设计方案,通过学习理解阿里的fusion.design的设计思想并结合手机淘宝H5版的px布局问题。逐渐有了一些想法,这里进行综合整理,也算是抛砖引玉吧。1、rem和vwrem和vw都是为了解决移动端适配问题。rem方案中最成功的就是淘宝的lib-flexible了,它是通过javascript将整个布局分割成10份,从而进行有效布局。不过有计算dpr的问题,在一些dpr比较怪异的手机上会出现脱相的问题。后来又产生了vw布局,使用了vw之后,也再无需通过javascript的帮助进行布局的切分,而是自动的将整个布局切割为等分的100份,也就是1vw = 1%的页面宽度。1.1、 rem的问题在奇葩的dpr设备上表现效果不太好,比如 一些华为的高端机型 用rem布局会出现错乱。设置根字体大小的方式有两种,一种是媒体查询,优点:不需要额外使用js去更改html的字体,缺点:不连续,或者说并能完* 全实现对所有设备的布局规范统一;另一种是js动态更改html字体,优点:连续;缺点:不如直接写媒体查询的体验好;不支持css3 calc的需要大量密集的 @media hack;使用iframe引用也会出现问题;需要解决在ios上的1px边框问题,但是这个在lib-flexible中已经解决:(1px变2px, 又被 initial-scale=0.5 缩小了一半rem需要引入一个lib库html的font-size设置到12px以下还是会按照12px=1rem来计算,这样所有使用了rem单位的尺寸都是错的1.2、vw的问题支持程度不太好,安卓4.4以下都不支持1.3、它们共同的问题都需要计算以达到适配的目的额外引入工具,在编译阶段完成转换UI的回归测试不友好。毕竟设计稿是px,而页面是rem或vm。都是相对单位。rem 的比例是可以通过控制 html 字体大小来控制的,而vw的比例是固定的。无法和web项目共用统一套工程化方案,因为web项目不需要使用rem和vw单位。2、移动端布局的初衷可以轻松搞定任意布局通过设计稿,可以让应用在不同的设备上有完美的体验效果虽然rem和vw可以很好完成它们的初衷,不过同时它们也是有代价的(就是它们存在的问题)。那有没有一种方案可以规避掉以上rem和vw的问题又可以很好的完成初衷哪?2.1、一个新的Units单位(该小节摘自https://fusion.design/design/…)DP为UI设计中的唯一可用单位由于DP在不同设备中的叫法不同,且用于描述字号的单位有所不同(如SP,PT),但其基本计算方法和原则相同且通用,所以在设计过程中,我们考虑到严谨性,统一采用只写数字不带单位的方法书写。选用DP的原因像素密度PPI:指每英寸包含的像素(Px)个数如图同一物理尺寸(肉眼所见尺寸)下,低密度显示器的像素个数明显小于高密度显示器的像素个数,所以像素(PX)在多变的设备和分辨率下不是一个稳定可用的单位与密度无关的像素(DP):设备独立像素如图,DP与PX的对比可见,DP可以自适应屏幕的密度,不管屏幕密度怎么变化,实际显示的物理尺寸相同,DP可以保证物理尺寸的一致性,DP是目前最适合UI设计的单位,同时也是使代码语言相通的尺寸。转换公式当屏幕的PPI为160时,1DP=1PX;例:Iphone4,Iphone5,Iphone6,PPI为326,在这些屏幕之下1DP=2PXDP=(PX160)/PPIPX=DP(PPI/160)切图规则DP是与开发代码共用的语言,但一些需要置入的jpg,png等图片非矢量,依旧采用px作为单位,这个时候我们需要将图片适配到不同PPI的屏幕中去。图示,为一块banner适配到不同分辨率屏幕时的像素值:但实际场景中,无法为各种屏幕做切图适配,我么遵循大图可压缩小,小图不可变大的原则:【Mobile】选择3x图输出,适配于ios和andirod【Web】选择2x的图输出,适配普通屏幕和retina屏幕画布设置规则【Mobile】选择375667作为绘图尺寸【Andriod】 选择360640作为绘图尺寸【Web】使用1440宽作为绘图尺寸3、具体实现主要思路设计稿中统一使用dp作为像素单位,具体规则参考上面的切图规则和画布设计规则rem和vw多多少少存在各种问题,所以统一使用px作为实现单位web和wap可以使用同一套工程方案实现设计稿的dp到真实应用中px的映射关系,并且px会随着设备窗口大小的改变而改变当然,如果稿子是px的也可以手动将px转换为dp。想要实现这个整体方案,核心就在于第4条(实现设计稿的dp到真实应用中px的映射关系),并且这个过程只靠工程化的编译阶段是无法完美解决的,必须和浏览器运行时一起配合工作才能够达成我们的目标。前提业务模块的css不可以抽离为独立的css文件,必须输出在js文件中(style-loader的能力),这样才有改变css内容的基本能力。定义一个尺寸单位dp,标识这是在设计稿上的尺寸(类似于小程序中的rpx)。并不是所有的px都需要做弹性转换的,对于需要做弹性转换的容器的px统一改为dp,否则继续使用px。假设我们根据Mobile设计稿定义一个移动端H5的容器元素:<div class=“box”> <div class=“tip”>this is tip</div></div>.box { /* 这里使用的单位为dp,表示需要根据设备大小进行弹缩 / width: 100dp; height: 150dp; background: red;}.box .tip { / 使用的单位为px,不需要根据设备大小进行弹缩。无论设备怎么变化,该元素的宽高都是10像素。 */ width: 10px; height: 10px; background: blue;}最终,元素.box会根据设备的宽高的改变而改变自身的大小,下方就是.box元素在不同设备下的宽和高:设备宽度高度设计稿100dp150dpiPhone 5/SE85.33px128pxiPhone 6/7/8100px150pxiPhone 6/7/8 Plus110.4px165.60pxiPhone X100px150pxGalaxy S596px144px在实现这个功能必须先提供一个转换dp为px的帮助函数:unitParser。因为接下来的两种方式中都需要这个函数来帮助我们实现最终目的。const allowMiniPixel = () => { let allow = false; if (window.devicePixelRatio && devicePixelRatio >= 2) { let ele = document.createElement(“div”); ele.style.border = “.5px solid transparent”; document.body.appendChild(ele); allow = 1 === ele.offsetHeight; document.body.removeChild(ele); } return allow;}();function unitParser(unit) { let type = void 0 === unit ? “undefined” : getType(unit); if (“number” === type) { unit += “dp” } if (“string” !== type) { return unit; } let regExp = /^([\d.]+)(np|dp)?$/g; return unit.replace(regExp, (chars, count, suffix) => { count = Number(count) switch (suffix) { case “np”: // np不做转换。1np就是1px 100np就是100px break; case “dp”: default: // 注意这里375。说明的上文说了,设计稿是按照iphone 6的375进行设计的。 // deviceWidth为屏幕的宽度。iphone 5/SE为320、iphone 6/7/8为375 count = count / 375 * deviceWidth }; if (!allowMiniPixel && count < 1) { count = 1 } return count + “px”; })}3.2.1、方式一:styled-components + css-in-js + JSXVue:import styled from ‘vue-emotion’import unitParser from ‘./unitParser.js’;const box = styled(‘div’) width: ${unitParser("100dp")}; height: ${unitParser("150dp")}; background: red;const tip = styled(div) width: 10px; height: 10px; background: blue;new Vue({ render() { return ( <box> <tip>this is a tip</tip> </box> ) }})react:import styled from ‘styled-components’;import unitParser from ‘./unitParser.js’;const Box = styled.div width: ${unitParser("100dp")}; height: ${unitParser("150dp")}; background: red;const Tip = styled.div width: 10px; height: 10px; background: red;render( <Box> <Tip>this is a tip</Tip> </Box>)注意:使用此方案需要注意和sass、post-css等工具的结合使用问题,会增加一定的工程复杂度。另外这种方案会产生大量的元素style属性,导致dom复杂度增加。3.2.2、方式二:在浏览器运行时动态计算自定义一个webpack的css-loader,进行unitParser处理。function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === ‘undefined’) { return; } var head = document.head || document.getElementsByTagName(‘head’)[0]; var style = document.createElement(‘style’); style.type = ’text/css’; if (insertAt === ’top’) { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); }}var css = “.box {width: " + unitParser(“100dp”) + “; height: " + unitParser(“150dp”) + “; background: red;} .box .tip {width: 10px; height:10px; background: blue}";styleInject(css);3.2.3、优缺点优点:文章开头所提到的rem和vw都存在众多问题,该方案都可以完美解决。还可以和其他任何单位混合使用,这意味这使用这种方案的同时还可以使用之前的rem和vw方式。缺点:不管使用方式一还是方式二,都会对项目的工程化复杂度增加,不过和目前处理rem以及vm的px2rem以及px2viewport等工具相比,这点复杂度根本不值一提。增加了js bundle文件的体积,减少了css文件的体积。不过没有整体的体积没有增加,仅仅是将部分业务的css内容搬到了js文件里。最后借用了大部分工程化的能力,主要还是用来css in js的能力但是这样有个缺点。就是视窗大小改变的时候会出现布局错乱。不过影响不大,因为真机里是不会出现窗口大小改版的。而且这一套方案完全抹平了web和wap的差异性,在开发层面完全一直。需要缩放就用dp,不需要就用np和px作为单位即可这套方案还是蛮不错的,比rem和vw好很多。确实属于第三代移动端布局方案了 ...

April 12, 2019 · 2 min · jiezi

Css-移动端适配总结

前言工作以后,大部分的业务工作都是基于移动端H5的,开发过程中学习了很多东西,遇到过许多问题,诸如rememcss pxdevice px等,本文纯属个人的归纳总结,如有问题,请指出亲喷~PC端本文主要是讲解移动端的响应式布局, 但是在真正进入之前, 先了解一些概念。device px(设备像素)和 css px(css像素)通常在PC端上面,我们并不需要考虑设备像素和css像素之间的差别,以目前的pc来看,1个设备像素通常等于1个css像素。可以使用screen.width/height来获取我们屏幕的宽高设备像素。screen.width // 1920screen.height // 1080如果你给一个元素的宽度为width: 192px; 那么你的屏幕上(假设你的屏幕宽度像素为1920)可以在一行上显示10个该元素。原理则是因为我们的PC中1个设备像素等于1个css像素。 当用户放大或者缩小屏幕时(按住ctrl+滚动鼠标轮,也就是改变zoom值),则有所不同。此时,我们的设备像素仍然没有改变,还是19201080,css像素的数量也没有改变,但是css像素大小变了。 假设放大到200%, 那么1个css像素就等于两个设备像素, 以此类推。 以下是引用ppk大神的三张图片, 下面深蓝色为设备像素, 上面浅蓝色为css像素正常情况下: 缩小时: 放大时: screen.width/height 和 window.innerWidth/innerHeightscreen.width/heihgt取的是屏幕的宽高,单位是是css像素。 window.innerWidth/innerHeight取的是网页区域的宽高, 单位是css像素。 当你改变zoom值时, screen不会改变, innerWidth/height会改变。viewport的概念viewport是一个限制html元素的功能, 可以理解为html元素的上一层元素。听起来有点难以理解, 下面讲一个例子: 假设, 你给某个div元素设置了width:50%的样式后, 当你缩小放大浏览器的时候,你会发现div元素总是占据了50%的宽度,我们知道,宽度百分比是依赖它的包裹元素(假设是body), 那么问题就回到了body的宽度身上。通常在没有设置宽度的情况下,所有块级元素都占用其父级宽度的100%。所以body和html元素一样宽。那么html元素有多宽呢,默认情况下它和浏览器窗口一样宽,这也就是为什么div总是占据浏览器宽度的50%,而html元素则是受限于viewport(和viewport占据一样的宽度),换句话说,viewport完全等于浏览器窗口,而且它不是HTML语言元素,所以你无法通过使用css对其进行影响。 我们可以通过document.documentElement.clientWidth/clientHeight来获取viewport的宽高, 它的单位是css像素。clientWidth/Height 和 window.innerWidth/innerHeight上面两者都能够获取网页区域大小, 但是他们之间还是有区别的。 前者不包含滚动条, 后者包含。html元素的大小我们可以通过document.documentElement.offsetWidth/offsetHeight来获取html元素的宽高, 它的单位是css像素。event事件和媒体查询event的三对属性:pageX/Y: 给出CSS像素中相对于html元素的坐标clientX/Y: 给出CSS像素中相对于viewport的坐标screenX/Y: 给出设备像素中相对于屏幕的坐标媒体查询:基于viewport(documentElement .clientWidth/Height)@media all and (max-width: 400px)基于屏幕(screen.width)@media all and (max-device-width: 400px)Mobile在默认情况下,一般来讲,移动设备上的viewport都是要大于浏览器可视区域的,这是因为考虑到移动设备的分辨率相对于桌面电脑来说都比较小,所以为了能在移动设备上正常显示那些传统的为桌面浏览器设计的网站,移动设备上的浏览器都会把自己默认的viewport设为980px或1024px(也可能是其它值,这个是由设备自己决定的),但带来的后果就是浏览器会出现横向滚动条,因为浏览器可视区域的宽度是比这个默认的viewport的宽度要小的。下图列出了一些设备上浏览器的默认viewport的宽度。以下是关于各浏览器的viewport 三个viewport前面介绍了viewport的概念, 但是在移动端的时候, viewport并不那么容易理解, ppk在移动端提出了三个viewport的概念。 Layout viewport 也就是布局viewport,即默认的浏览器viewport , 并且可以通过document.documentElement.clientWidth 来获取。 图片引用自深入理解viewport visual viewport layout viewport 的宽度是默认的浏览器viewport,所以我们还需要一个viewport来代表网页区域的大小,ppk把这个viewport叫做 visual viewport。visual viewport的宽度可以通过window.innerWidth 来获取。 图片引用自深入理解viewport ideal viewport 有了两个viewport并不ok, 因为我们既不想让用户滚动滚动条来浏览我们的网页,也不想用户盯着缩小了的pc网页浏览, 所以有了第三个viewport。 所谓的ideal viewport则是当layout viewport等于屏幕的宽度, 如ip6,它的ideal viewport就是375px。设置viewport开发过h5的应该都知道, 我们经常会把下面这句代码复制到head标签中:<meta name=“viewport” content=“width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0”>它的作用其实就是设置了ideal viewport。以下是它的6个属性:keyvaluewidth设置layout viewport 的宽度,为一个正整数,或字符串"width-device"initial-scale设置页面的初始缩放值,为一个数字,可以带小数minimum-scale允许用户的最小缩放值,为一个数字,可以带小数maximum-scale允许用户的最大缩放值,为一个数字,可以带小数height设置layout viewport 的高度,这个属性对我们并不重要,很少使用user-scalable是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许那么如果我们想设置ideal viewport, 只需要把width设置成width-device或者把initial-scale设置成1.0就可以了。 前者比较容易理解, 后者设置成1就可以是为什么? 首先要理解设置成1.0就是意味着没有缩放,而这样却可以达到ideal viewport的效果, 那么很明显, 缩放是相对于 ideal viewport来进行缩放的,当对ideal viewport进行100%的缩放,也就是缩放值为1的时候,不就得到了 ideal viewport。 如果两个属性都能设置ideal viewport, 那么当两个属性冲突时怎么解决?遇到这种情况时,浏览器会取它们两个中较大的那个值。例如,当width=400,ideal viewport的宽度为320时,取的是400;当width=400, ideal viewport的宽度为480时,取的是ideal viewport的宽度。css像素和设备像素在移动端中, 1个css像素并不等于1个设备像素, 而是取决于设备像素比(物理像素(设备像素)/独立像素(css像素)),像Iphone的Retina屏幕, 就有2倍屏(ip6s)、3倍屏(ip6 plus), 也就是设备像素比的值分别是2和3,即1个css像素等同于4个设备像素或者9个, 如图: 并且, 我们可以通过window.devicePixelRatio来获取设备像素比dpr。1px的产生和解决问题的产生公司的设计大佬通常给的设计稿是基于ip6s的, 也就是750px(i6s的屏幕是375px,而且是上面说的两倍屏,所以有750个物理像素)。假设设计稿上面有个1px的border,我们通常直接这样写:border { border: 1px solid #ccc;}然后设计审核的时候就被打回来了, 因为设计觉得变大了,也就是他觉得是2px的线了。 究其原因,是因为设计稿是750px, 里面的1px实际上在真机只有0.5px,所以就有了著名的1px问题。 问题的解决 1.直接使用0.5px在iOS8下,苹果系列都已经支持0.5px了,那么意味着在devicePixelRatio = 2时,我们可以借助媒体查询来处理:著作权归作者所有。@media (-webkit-min-device-pixel-ratio: 2) { .border { border-width: 0.5px }}这种使用简单,但是兼容性不太好。2.使用border-image或者background 也就是拿一张图片,一半透明,一半是我们想要的颜色,然后填充上去, 具体的例子就不讲了, 这种基本没啥人会用, 改个颜色还要修改图片,太麻烦了。 3.box-shadow.box-shadow-1px { box-shadow: inset 0px -1px 1px -1px #c8c7cc;}这种颜色有阴影, 估计过不了设计大佬的那关。 4.PostCSS的插件postcss-write-svg直接借助插件帮助我们实现, 其实也就是postcss帮我们生成图片而已。@svg 1px-border { height: 2px; @rect { fill: var(–color, black); width: 100%; height: 50%; } } .example { border: 1px solid transparent; border-image: svg(1px-border param(–color #00b1ff)) 2 2 stretch;}最后Postcss会把对应的css编译出来, 这种兼容性好, 就是依赖于插件。.example { border: 1px solid transparent; border-image: url(“data:image/svg+xml;charset=utf-8,%3Csvg xmlns=‘http://www.w3.org/2000/svg' height=‘2px’%3E%3Crect fill=’%2300b1ff’ width=‘100%25’ height=‘50%25’/%3E%3C/svg%3E”) 2 2 stretch}5.伪类 + transform 实现 原理是把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。.scale-1px{position: relative;border:none;}.scale-1px:after{content: ‘’;position: absolute;bottom: 0;background: #000;width: 100%;height: 1px;-webkit-transform: scaleY(0.5);transform: scaleY(0.5);-webkit-transform-origin: 0 0;transform-origin: 0 0;}这种兼容好, 但是会和伪类冲突, 也是我司采用的方式。 6.缩小viewport 原理是使用meta标签中的viewport, 也就是上面所说的设置viewport, 将整个页面缩小0.5倍, 这个主要是麻烦在其他的元素也要跟着放大一倍再缩小, 为了这个小问题而这样, 似乎有点得不偿失。 然而, 淘宝的flexible方案(rem布局,见下文)帮我们搞定了这个问题。flexible和rem上面提到了flexible和rem的布局方案, 在刚推出的时候, 确实很火, 公司的一些项目目前仍然是采用该方案, 这里简单的说下其原理。 px、em、rempx: px在前面已经讲了, 就是一个固定单位。em: em作为font-size的单位时,其代表父元素的字体大小,em作为其他属性单位时,代表自身字体大小。rem: rem作用于非根元素时,相对于根元素字体大小;rem作用于根元素字体大小时,相对于其出初始字体大小。/ 作用于根元素,相对于原始大小(16px),所以html的font-size为32px*/html {font-size: 2rem}/* 作用于非根元素,相对于根元素字体大小,所以为64px */p {font-size: 2rem}flexible rem布局原理 flexible rem布局原理即是把设计稿等比宽的切成100份, 假设每份的单位是x, 那么我们在布局的时候就可以以x为单位, 将设计稿等比例的放大缩小到对应的屏幕了,这样就不用为各个屏幕做适配。 然而像上面所说的x是不存在的, 不过好在我们有rem, 只要我们将rem设置成1x,那么开发过程中,不就达到我们的目的了吗? 如何将rem设成1x呢? 回想一下, 我们是不是能取得viewport的宽度(document.documentElement.clientWidth),我们能取得设备像素比(window.devicePixelRatio), 我们能够设置html的样式(html.style.fontSize = ‘…’), 所以简单的实现方案就有了document.documentElement.style.fontSize = document.documentElement.clientWidth / 100 + ‘px’;当然,flexible并不这么简单,它还对于不同dpr做了处理, 它帮你处理了2倍屏、3倍屏等情况,通过设置viewport的缩放比,这也就是上面所说的0.5px处理方案之一, 具体的这里不再赘述, 有兴趣的同学可以看看原文。 最后,移动端 iOS 8 以上以及 Android 4.4 以上已经有了vw\vh单位, 1vw1vh相当于viewport的百分之一宽/高,也就是我们上面所说的x单位, 如果你的手机支持该api, 也可以使用该单位方案。为什么不用rem方案依稀记得, 某次使用了rem处理活动页的时候, 被设计大佬驳回了。 大佬认为, 当用户使用更大的屏幕的时候, 他应该能看到更多的内容, 而且设计稿被放大或者缩小的话, 会失去他原来的感觉。 所以, 对于rem方案其实可能已经不太适合当前的情况了, 毕竟使用媒体查询和px以及em就能解决各种响应式问题, 虽然效率会比较低下, 而关于这个, 也恰好看到了有人在知乎上提了这么个问题, 有兴趣的可以前去围观。 为什么很多web项目还是使用 px,而不是 rem?总结本文多是概念上的,也参考了许多文章,要真正理解还需要多参考实际项目。参考&引用移动前端开发之viewport的深入理解 A tale of two viewports — part one A tale of two viewports — part two Meta viewport 7 种方法解决移动端 Retina 屏幕 1px 边框问题 再谈Retina下1px的解决方案 vh,vw单位你知道多少? Rem布局的原理解析 ...

April 7, 2019 · 2 min · jiezi

移动端适配方案 (有demo)

移动端 Web 页面适配方案其中方案 1.3.4.5的demo地址移动端适配方案移动端 Web 页面,即常说的 H5 页面、手机页面、webview 页面等。手机机设备屏幕尺寸不一,做移动端的 Web 页面,需要考虑在安卓/IOS 的各种尺寸设备上的兼容,这里总结的是针对移动端设备的页面,设计与前端实现怎样做能更好地适配不同屏幕宽度的移动设备。适配的目标在不同尺寸的手机设备上,页面“相对性的达到合理的展示(自适应)”或者“保持统一效果的等比缩放(看起来差不多)”。<!– markdown-to-slides index.md -o index.html -s slide.css –>I. 概念的理解要搞懂移动端的适配问题,就要先搞明白像素和视口。像素在移动端给一个元素设置 width:200px 时发生了什么?这里的 px 到底是多长呢?像素是网页布局的基础,但是我们一直在用直觉使用它。iphone6 有 750 的物理像素,但是屏幕宽度像素是 375px???1.设备像素 device pixel设备像素是物理概念,指的是设备中使用的物理像素,任何设备屏幕的物理像素的数量都是固定不变的。比如 iphone6 的分辨率 1334 x 750px ,750px 指的是设备物理像素2.逻辑像素 css pixelcss 像素是 web 编程的概念,指的是 css 样式中使用的逻辑像素css px 是一个相对单位 相对的是设备像素我们网页 css 和小程序用的是逻辑像素eg:iphone6 使用的是 retina 视网膜屏幕2px x 2px 的设备像素代表 1px z 1px 的 css 像素所以设备像素 1334 x 750 css 逻辑像素是 667 x 375那么,我们现在再来说说一个元素 width:200px 以后会怎么样。这个元素跨越了 200 个 CSS 元素,200 个 CSS 元素相当于多少个设备像素取决于两个条件:页面是否缩放屏幕是否为高密度这两方面后面再解释,先梳理一下手机硬件之间的关系,注意这里使用的都是物理像素。以 iPhone5 为例,我们已知的是:1.分辨率 1136pt x 640pt指屏幕上垂直有 1136 个物理像素,水平有 640 个物理像素2.屏幕尺寸 4 英寸注意英寸是长度单位,不是面积单位。4 英寸指的是屏幕对角线的长度。3.屏幕像素密度 326ppi屏幕像素密度(Pibel Per Inch)简称 ppi ,单位是 dpi(dot per inch)。这里指屏幕水平或垂直每英寸有 326 个物理像素。原则上来说,ppi 越高越好,因为图像会更加细腻清晰。ppi 是可以通过 分辨率 和 屏幕尺寸 计算得到的:<img src="./ppi.png"/>这个网站列出了很多设备的分辨率和屏幕尺寸,并且计算了 ppi。这个网站列出了很多设备的分辨率和屏幕尺寸,并且计算了 ppi。视口桌面浏览器中,浏览器窗口就是约束你的 CSS 布局视口(又称初始包含块)。它使所有 CSS 百分比宽度推算的根源,它的作用是 CSS 布局限制了一个最大宽度,视口的宽度和浏览器窗口宽度一致。但是在移动端,情况就很复杂了。布局视口 layout viewport在手机上,视口与移动端浏览器屏幕宽度不再相关联,是完全独立的,这个浏览器厂商定的视口被称为布局视口。布局视口我们是看不见的,只知道网页的最大宽度是 980px ,并且被缩放在了屏幕内。可以这样设置布局视口的宽度:<meta name=“viewport” content=“width=640” />媒体查询与布局视口@media (min-width: 700px) { …;}document.documentElement.clientWidth/Height 返回布局视口的尺寸视觉视口 visual viewport有了 layout viewport,我们还需要一个视口用来承载它,这个视口可以简单的认为是手持设备物理屏幕的可视区域,视觉视口是用户正在看到的网页的区域,大小是屏幕中 CSS 像素的数量。window.innerWidth/Height 返回视觉视口的尺寸很明显,visual viewport 的尺寸不会是一个固定的值,甚至每款设备都可能不同。大致列几种常见设备的 visual viewport 尺寸:iPhone4iPhone5S: 320*480pxiPhone6iPhone6S: 375627pxiPhone6 Plus~iPhone6S Plus: 414736px以 iPhone4S 为例,会在其 320px② 的 visual viewport 上,创建一个宽 980px 的 layout viewport,于是用户可以在 visual viewport 中拖动或者缩放网页,来获得良好的浏览效果;布局视口用来配合 CSS 渲染布局,当我们定义一个容器的宽度为 100%时,这个容器的实际宽度是 980px 而不是 320px,通过这种方式大部分网页就能以缩放的形式正常显示在手机屏幕上了。理想视口(完美视口) ideal viewport布局视口明显对用户是不友好的,完全忽略了手机本身的尺寸。所以苹果引入了理想视口的概念,它是对设备来说最理想的布局视口尺寸。理想视口中的网页用户最理想的宽度,用户进入页面的时候不需要缩放。<meta name=“viewport” content=“width=device-width” />定义理想视口是浏览器的事情,并不能简单地认为是开发者定义的,开发者只能使用。screen.width/height 返回理想视口的尺寸,有严重的兼容性问题—可能返回两种值:理想视口的尺寸(下载浏览器)屏幕的设备像素尺寸(内置浏览器)Screen size tests 和 Understanding viewport 可以测试你的设备的 screen.width 值,同一设备的不同浏览器返回的值可能是不一样的。这一情况主要发生在默认浏览器和下载浏览器(如 UC、Chrome)之间。关于 3 个视口,PPK已经做了非常棒的阐释,你也可以在 StackOverflow 上找到一些对此描述的相互补充,例如:1, 2,有兴趣的童鞋也可以看看缩放缩放是在放大或缩小 CSS 像素,比如一个宽度为 200px 的元素无论放大,还是 200 个 CSS 像素。但是因为这些像素被放大了,所以 CSS 像素也就跨越了更多的设备像素。缩小则相反。缩放与视口缩放会影响视觉视口的尺寸页面被用户放大,视觉视口内 CSS 像素数量减少;被用户缩小,视觉视口内 CSS 像素数量增多就行了。这个道理应该是不难想的。用户缩放不会影响布局视口在下载浏览器中,可以这么算(理想视口与视觉视口的比):zoom level = screen.width / window.innerWidth禁止缩放<meta name=“viewport” content=“user-scalable=no” />设置缩放<meta name=“viewport” content=“initial-scale=2” />使用 initial-scale 有一个副作用:同时也会将布局视口的尺寸设置为缩放后的尺寸。所以 initial-scale=1 与 width=device-width 的效果是一样的。完美视口解决各种浏览器兼容问题的理想视口设置<meta name=“viewport” content=“width=device-width,initial-scale=1” />设备像素比屏幕是否为高密度也会影响设备像素和 CSS 像素的关系。在缩放程度为 100%(这个条件很重要,在后面会说到)时,他们的比例叫做设备像素比(device pixel ratio):dpr = 设备像素 / CSS 像素可以通过 JS 得到: window.devicePixelRatio设备像素比也和视口有关:dpr = 屏幕横向设备像素 / 理想视口的宽viewport 特性NameValuedescriptionwidth正整数或 device-width定义视口的宽度,单位为像素height正整数或 device-height定义视口的高度,单位为像素initial-scale[0.0-10.0]定义初始缩放值minimum-scale[0.0-10.0]定义缩小最小比例,它必须小于或等于 maximum-scale 设置maximum-scale[0.0-10.0]定义放大最大比例,它必须大于或等于 minimum-scale 设置user-scalableyes/no定义是否允许用户手动缩放页面,默认值 yeswidthwidth 被用来定义 layout viewport 的宽度,如果不指定该属性(或者移除 viewport meta 标签),则 layout viewport 宽度为厂商默认值。initial-scale如果想页面默认以某个比例放大或者缩小然后呈现给用户,那么可以通过定义 initial-scale 来完成。<meta name=“viewport” content=“initial-scale=2” />那么用户将会看到 2 倍大小的页面内容。maximum-scale在移动端,你可能会考虑用户浏览不便,然后给予用户放大页面的权利,但同时又希望是在一定范围内的放大,这时就可以使用 maximum-scale 来进行约束。<meta name=“viewport” content=“initial-scale=1,maximum-scale=5” />假设页面的默认缩放值 initial-scale 是 1,那么用户最终能够将页面放大到这个初始页面大小的 5 倍。minimum-scale类似 maximum-scale 的描述,不过 minimum-scale 是用来指定页面缩小比例的。通常情况下,为了有更好地体验,不会定义该属性的值比 1 更小,因为那样页面将变得难以阅读。user-scalable如果你不想页面被放大或者缩小,通过定义 user-scalable 来约束用户是否可以通过手势对页面进行缩放即可。<meta name=“viewport” content=“user-scalable=no” />II. 移动端适配方案1.使用百分比+媒体查询 test.1.html2.使用 flexbox3.使用 rem + viewport test.3.html4.使用 rem test.4.html5.固定布局视口宽度,使用 viewport 进行缩放 test.5.html在 iphone6p 下 test.1.html 和 test.3.html 适配对比<img src="./iphone6p1-3.png"/>rem 是什么相对长度单位,相对于根元素(即 html 元素)font-size 计算值的倍数兼容性<img src="./rem.png"/>浏览器默认为 16px 可能造成 rem 计算上的麻烦和多位小数,所以,我们也可以使用 100px 初始化根元素:<img src="./computed.png" height=“220” width=“650”/>1.使用百分比+媒体查询meida queries 的方式可以说是我早期采用的布局方式,它主要是通过查询设备的宽度来执行不同的 css 代码,最终达到界面的配置。核心语法是:@media screen and (max-width: 600px) { /当屏幕尺寸小于600px时,应用下面的CSS样式/ /你的css代码/}优点media query 可以做到设备像素比的判断,方法简单,成本低,特别是对移动和 PC 维护同一套代码的时候。目前像 Bootstrap 等框- 架使用这种方式布局图片便于修改,只需修改 css 文件调整屏幕宽度的时候不用刷新页面即可响应式展示缺点代码量比较大,维护不方便为了兼顾大屏幕或高清设备,会造成其他设备资源浪费,特别是加载图片资源为了兼顾移动端和 PC 端各自响应式的展示效果,难免会损失各自特有的交互方式2.以天猫首页为代表的 flex 弹性布局[表示怀疑]它的 viewport 是固定的:<meta name=“viewport” content=“width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no”/>高度定死,宽度自适应,元素都采用 px 做单位。随着屏幕宽度变化,页面也会跟着变化,效果就和 PC 页面的流体布局差不多,在哪个宽度需要调整的时候使用响应式布局调调就行(比如网易新闻),这样就实现了『适配』。3.rem + viewport 缩放这也是淘宝使用的方案,根据屏幕宽度设定 rem 值,需要适配的元素都使用 rem 为单位,不需要适配的元素还是使用 px 为单位。实现原理如 iphone6 plus 的 dpr 为 3, 则页面整体放大 3 倍, 1px(css 单位)在 plus 下默认为 3px(物理像素)然后 viewport 设置为 1/3, 这样页面整体缩回原始大小. 从而实现高清。这样整个网页在设备内显示时的页面宽度就会等于设备逻辑像素大小,也就是 device-width。 这个 device-width 的计算公式为:设备的物理分辨率/(devicePixelRatio * scale), 在 scale 为 1 的情况下,device-width = 设备的物理分辨率/devicePixelRatio 。当设计以 iphone6 为标准,出 750px 的设计稿时,此时 dpr=2width = document.documentElement.clientWidth = 375px;rem = 375px / 7.5 = 50px; // font-size = 50pxdpr = 2 时, 1rem = 100px, initial-scale=0.5, 缩放为0.5。4.rem 实现根据不同屏幕动态写入 font-size,以 rem 作为宽度单位,固定布局视口。<meta name=“viewport” content=“width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no”/>以 640px 设计稿和 750px 的视觉稿,网易这样处理的:var width = document.documentElement.clientWidth // 屏幕的布局视口宽度var rem = width / 7.5 // 750px设计稿将布局视口分为7.5份var rem = width / 6.4 // 640px设计稿将布局视口分为6.4份在 750px 设计稿上://在ipone6上:width = document.documentElement.clientWidth = 375px;rem = 375px / 7.5 = 50px; // font-size = 50px//在ipone5上:width = document.documentElement.clientWidth = 320px;rem = 320px / 7.5 = 42.667px; // font-size = 42.667px;百分比适配,没有 1px 适配,有字体大小适配。通过以下代码来控制 rem 基准值(设计稿以 720px 宽度量取实际尺寸)documentElementdevicePixelRatio!(function(d) { var c = d.document var a = c.documentElement var b = d.devicePixelRatio var f function e() { var h = a.getBoundingClientRect().width, g if (b === 1) { h = 720 } if (h > 720) h = 720 //设置基准值的极限值 g = h / 7.2 a.style.fontSize = g + ‘px’ } if (b > 2) { b = 3 } else { if (b > 1) { b = 2 } else { b = 1 } } a.setAttribute(‘data-dpr’, b) d.addEventListener( ‘resize’, function() { clearTimeout(f) f = setTimeout(e, 200) }, false ) e()})(window)5.固定布局视口宽度,使用 viewport 进行缩放如:荔枝 FM、网易应用固定布局视口,宽度设置固定的值,总宽度为 640px,根据屏幕宽度动态生成 viewport。(设计稿应该是 640px 的)<meta name=“viewport” content=“width=640, minimum-scale = 0.5625, maximum-scale = 0.5625, target-densitydpi=device-dpi”/>网页宽度始终为 640px。缩放比例 scale 为:var scale = window.screen.width / 640百分比适配,部分 1px 适配,没有字体适配。微信新增尺寸单位 rpx新增了尺寸单位。在写 CSS 样式时,开发者需要考虑到手机设备的屏幕会有不同的宽度和设备像素比,采用一些技巧来换算一些像素单位。WXSS 在底层支持新的尺寸单位 rpx ,开发者可以免去换算的烦恼,只要交给小程序底层来换算即可,由于换算采用的浮点数运算,所以运算结果会和预期结果有一点点偏差。rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为 750rpx。如在 iPhone6 上,屏幕宽度为 375px,共有 750 个物理像素,则 750rpx = 375px = 750 物理像素,1rpx = 0.5px = 1 物理像素。设备物理像素css 像素rpx->pxpx->rpxiPhone5320px320px1rpx = 0.42px1px = 2.34rpxiPhone6750px375px1rpx = 0.5px1px = 2rpxiPhone6+1080px414px1rpx = 0.552px1px = 1.81rpx建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。III. 响应式和自适应自适应是最早出现的,后面才有了响应式。响应式布局等于流动网格布局,而自适应布局等于使用固定分割点来进行布局。响应式的基本概念是(Responsive design):简而言之,就是一个网站能够兼容多个终端——而不是为每个终端做一个特定的版本。自适应的解释(Adaptive design):自适应布局给了你更多设计的空间,因为你只用考虑几种不同的状态。如果用响应式布局来处理的话,用不同设备(电脑、平板、手机)去访问此页面,最后看到的布局和内容有很大不同。而如果用自适应布局去处理的话,那不管访问设备如何的不同(下图是三台尺寸不一样的手机),最后看到的页面内容和布局基本上还是一样的,就是尺寸略有不同。响应式京东云官网 (栅格系统 + 媒体查询)pro-list-hd hidden-xsMicrosoft (栅格系统 + 媒体查询)col-6 pad-6x华为 (栅格系统 + 媒体查询 )col-sm-4 col-pd-12 col-mb-12自适应携程淘宝实现响应式有哪几种方式?1.使用媒体查询(Media Query) 结合 rem 字体// 如果大于1000px,就用:对应PC端页面@media screen and (min-width: 1000px) { …;}// 如果在小于1000px和大于768px之间: 对应平板端页面@media screen and (max-width: 1000px) and (min-width: 768px) { …;}// 如果小于768px: 对应手机端页面@media screen and (max-width: 768px) { …;}2.使用插件bootstrap栅格化布局,本例利用 bootstarp 的栅格系统<img src="./sgxt.png" /><img src="./sg.png" /><img src="./768.png" height=“170”/><img src="./769.png" height=“170”/><img src="./992.png" height=“170”/>3.使用弹性布局(flex)flexVI. 移动端适配总结1 像素边框高清高清图问题1 像素边框高清在移动端 web 开发中,UI 设计稿中设置边框为 1 像素,前端在开发过程中如果出现 border:1px,测试会发现在某些机型上,1px 会比较粗,即是较经典的移动端 1px 像素问题。项目中引入:border-1px.css,然后通过控制元素的 before 和 after 伪元素,改变 border 大小或颜色。上面说到的淘宝的实现方式即 rem+viewport 缩放来实现。div { width: 1px; height: 100%; display: block; border-left: 1px solid #e5e5e5; -webkit-transform: scale(0.5); transform: scaleX(0.5);}缺点:圆角无法实现,实现 4 条边框比较麻烦,并且只能单独实现,如果嵌套,会对包含的效果产生不想要的效果,所以此方案配合:after 和 before 独立使用较多。box-shadow利用 CSS 对阴影处理的方式实现 0.5px 的效果-webkit-box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.5);优点:基本所有场景都能满足,包含圆角的 button,单条,多条线。缺点:颜色不好处理, 黑色 rgba(0,0,0,1) 最深的情况了。有阴影出现,不好用。大量使用 box-shadow 可能会导致性能瓶颈。四条边框实现效果不理想。图片实现使用 background-image 实现 1px 有两种方式: 渐变 linear-gradient 或直接使用图片(base64)。渐变 linear-gradient (50%有颜色,50%透明)单线条div { height: 1px; background-image: -webkit-linear-gradient(top, transparent 50%, #000 50%); background-position: top left; background-repeat: no-repeat; background-size: 100% 1px;}优点: 可以设置单条,多条边框 可以设置颜色缺点: 大量使用渐变可能导致性能瓶颈 代码量大 多背景图片有兼容性问题高清图问题1. 使用 srcset 标签(WebKit 最新特性 srcset 简介)<img src=“http://g.ald.alicdn.com/bao/uploaded/i1/TB1d6QqGpXXXXbKXXXXXXXXXXXX_!!0-item_pic.jpg_160x160q90.jpg" srcset=” http://img01.taobaocdn.com/imgextra/i1/803091114/TB2XhAPaVXXXXXmXXXXXXXXXXXX_!!803091114.jpg 2x, http://gtms04.alicdn.com/tps/i4/TB1wDjWGXXXXXbtXVXX6Cwu2XXX-398-510.jpg_q75.jpg 3x “/>2. 使用 js 自带的 Image 异步加载图片<img id=“img” data-src1x=“xxx@1x.jpg” data-src2x=“xxx@2x.jpg” data-src3x=“xxx@3x.jpg”/>var dpr = window.devicePixelRatioif (dpr > 3) { dpr = 3}var imgSrc = $(’#img’).data(‘src’ + dpr + ‘x’)var img = new Image()img.src = imgSrcimg.onload = function(imgObj) { $(’#img’) .remove() .prepend(imgObj) //替换img对象}3. 背景图片高清解决方法(对于 dpr=2,1 个 css 像素对应 4 个物理像素)对于 dpr=1,物理像素和 css 相同,图片高清。但对于 dpr=2,导致每个像素点实际上有 4 倍的普通像素点,反过来说,一个 CSS 像素点实际分成了四个,这样就造成了颜色只能近似选取,于是,我们看上去就变得模糊了。所以使用 2x 的图片就刚刚好。使用 media 来处理/* 普通显示屏(设备像素比例小于等于1)使用1倍的图 /.css { background-image: url(img_1x.png);}/ 高清显示屏(设备像素比例大于等于2)使用2倍图 /@media only screen and (-webkit-min-device-pixel-ratio: 2) { .css { background-image: url(img_2x.png); }}使用 image-set 来处理https://www.html.cn/book/css/….css { background-image: url(1x.png); /不支持image-set的情况下显示/ background: -webkit-image-set( url(1x.png) 1x, / 支持image-set的浏览器的[普通屏幕]下 / url(2x.png) 2x, / 支持image-set的浏览器的[2倍Retina屏幕] / url(3x.png) 3x / 支持image-set的浏览器的[3倍Retina屏幕] */ );}IV. 总结移动端 Web 页面适配方案移动端适配方案(上)移动前端开发之 viewport 的深入理解深入了解 viewport 和 pxMobileWeb 适配总结移动端适配方案(下)移动端 H5 页面高清多屏适配方案移动前端第一弹:viewport 详解前端移动端适配总结移动前端自适应适配布局解决方案移动端页面适配———多方案解析移动端适配之 REM移动端前端适配方案移动端适配方案一 弹性布局移动端适配方案移动端 1 像素边框问题的解决方案移动端适配问题<center>– End –</center> ...

March 25, 2019 · 5 min · jiezi