移动端布局基础概念指南

前言这篇文章主要讨论了移动端页面渲染与布局的一些基本概念例如 viewport 和 css 像素, 这些内容是了解移动端页面适配的基本前提. 在这篇文章中记录了这些基本的概念, 因为我并不从事移动端开发, 文章有误, 欢迎指正. 移动端渲染表现为了简单起见, 我们不从概念讲起, 我们先来看看一个固定宽度的元素被放置到移动端浏览器中会发生什么. 有如下代码: <!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>当然桌面浏览器渲染效果肯定在我们意料之中啦✨: 图片: 1920px下chrome渲染的效果: 而下面展示了图片展示了几个移动端浏览器渲染的结果: 图片: 移动端浏览器渲染效果: 这里我们简单的介绍一下图片中浏览器的版本, 这些浏览器在后文的截图中版本都是不会发生变化的. 浏览器版本平台chrome75windows10chrome75android9edge42android9Samsung9.3android9仔细观察我们发现:900px 宽的 div 没有占满屏幕, 也就是说屏幕的宽度比 900px 还要大. 那么屏幕宽度到底是多大呢, 我们插入一行代码来获取HTML元素的宽度: <script> document.write(`html元素的宽度是${document.documentElement.clientWidth}`);</script>不同浏览器的显示结果为: 他们都输出了同样的数据那就是 980 在某种程度上我们知道了页面的宽度, 原来页面是 980px 的宽度所以 900px 的div无法铺满一行. 对于移动浏览器它感知到自己的视口的宽度是 980px, 此时它渲染的比例就和一个 横向分辨率为 980px 的桌面显示器渲染的一样. 只不过移动终端的屏幕是一个小号的显示器而已.在没有更改页面的设置的情况下或者说默认的情况下, 移动终端的浏览器会按照 980px 的宽度来渲染页面, 然后再将页面缩放到适合屏幕的大小. ...

October 13, 2019 · 4 min · jiezi

移动开发兼容问题

1.安卓使用绝对定位布局与原生input有冲突如果绝对定位的元素在最下面,键盘弹起时,绝对定位元素会在键盘上面 解决办法: 使用flex布局实现,有一个flex-shrink可使用或者监听resize事件,将元素隐藏export function adapterPosition(selector) { if (/iphone/i.test(navigator.userAgent)) return const h = window.innerHeight; const dom = document.querySelector(selector) if (!dom) return const display = dom.style.display window.addEventListener('resize', () => { const height = window.innerHeight if (height < h) { dom.style.display = 'none' } else { dom.style.display = display } });}2.低版本浏览器,给input设置flex:1之后,将兄弟元素挤出了父元素空间具体原因待查,反正需要给input加一个display:block <div class="item"> <div class="name">验证码</div> <input class="jInput input" type="number" pattern="[0-9]*" placeholder="请输入短信验证码"> <button class="btn jSendVcodeBtn"> 发送验证码 </button></div>.item { margin-left: 15px; box-sizing: border-box; height: 60px; padding: 12px 15px 12px 0; overflow: hidden; background-color: #fff; color: #212121; position: relative; display: -webkit-box; display: flex; -webkit-box-align: center; align-items: center; border-bottom: 1px solid #f4f4f4; font-size: 16px;}.item .name { margin-right: 10px; min-width: 48px;}.item .input { display: block; // 需要加一个display:block outline: 0 none; -webkit-box-flex: 1; flex: 1; font-size: 16px; padding: 0; border-width: 0; box-shadow: none; -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text;}3.relative top失效关于relative元素top属性失效的原因,父元素没有设置高度,子元素top使用百分比的时候没有参照,此时可以使用px值 ...

July 13, 2019 · 1 min · jiezi

移动端input框问题

部分问题可参考:https://www.cnblogs.com/ljx20... 失焦之后webview没有回到原来位置input.addEventListener('blur', () => { setTimeout(() => { window.scrollTo(document.body.scrollLeft, document.body.scrollTop); }, 20)})不聚焦问题如果引入了fastclick,那么需要修改源码,如下 FastClick.prototype.focus = function(targetElement) { targetElement.focus();};如果还有问题,尝试监听input的focus,然后延时聚焦 this.jInput.addEventListener('focus', () => { setTimeout(() => { this.jInput.focus() }, 300)})如果没有引入fastclick,网上找其他原因吧,还没遇到过

July 13, 2019 · 1 min · jiezi

vwrem移动端自适应布局

本文同步发布于我的个人博客上 - vw+rem移动端自适应布局 不管是面试还是工作过程中,移动端的布局都比较常见,而移动端适配方法也是多种多样。一般来说,主要还是从viewport、rem、百分比几个点下手。 一般来说,开发过程中,我们希望能够直接按照设计图来开发,不管设计图是两倍还是三倍图,能够直接写设计图尺寸而不需要换算,同时有高质的设计图还原度,想想都比较爽。 这里介绍一种使用vw和rem来布局的方案。 该方案思路主要是,设置视口宽度为设备宽度,使用vw来动态设置根元素的font-szie,同时使用sass的css function来实现设计尺寸转rem的功能,从而实现一套不需要js计算而自动设置根元素font-szie的rem布局。 vw不了解vw的同学可以了解一下vw。vw表示当前视口宽度的百分之一。 rem布局过程中依赖于根元素的font-size属性,而如果设置一个固定的font-size,再使用rem来布局,会导致小屏手机视觉上觉得网页被放大,而大屏手机上则显得网页布局稀疏。 所以咱们需要根据收据的屏幕大小等比的设置font-size,从而实现大小屏手机视觉一致的效果。实现这个功能可以使用js在页面载入时,读取屏幕宽度,再根据设计图标准宽度做一些转换。 而使用vw天然就是一个根据屏幕宽度来做计算的长度单位,完美实现以上js计算功能。同时在移动端,vw的兼容性还不错,完全可以直接使用。 计算方法: // 设计图标准根元素字体 / 设计图标准宽度 * 100const vw = 16 / 750 * 100例如,设计图以6s为标准,2倍尺寸,宽度750px,而你设置根元素字体大小为16px,那么计算出的vw就是2.13333vw,直接在css中设置: html { font-size: 2.13333vw;}这个时候,我们在标准宽度下根元素实际字体大小是16px,如果到5s手机上面,则根元素实际字体大小为13.653px。根元素的字体大小变了,页面中使用rem来设置的边距、长度、字体大小都会发现改变,页面看起来像是缩小了一点点,但是所有的布局跟6s都是一样的。 6s显示效果: 5s显示效果: 可以看到效果是一致的,换行、截断都显示一致,视觉效果也是一致的。 横屏显示我们日常使用中,手机都是竖屏显示的,但是也会有横屏显示的时候。如果横屏显示,那么手机的宽度就变成了长度,长度就变成了宽度了,这个时候vw就显示的不对了。看下图,虽然显示没有乱,但是字变的很大了,复杂情况下布局估计也乱了。 还好有应对方法:vmin,这个属性表示视口宽度中最小的那个,竖屏下是vw,横屏下是vh。设置了vim之后显示效果就不错了。 自动转换rem设计图的设计尺寸一般都是2倍或者3倍,如果此时咱们自己转换成一倍的再去根据根元素计算rem那也太累了。 比如2倍设计图上面的56px,那么咱们需要:56 / 2 / 16,心态崩溃~~~ 算是不可能自己算的,找插件呗,这个轮子早就被造好了,postcss-pxtorem就是专门来干这个事情的,配置好设计尺寸,设计倍数,然后css里面直接写56px,插件会自己给你计算成 (56 / 2 / 16)px,是不是很棒。 加载配置一个插件也挺麻烦的,如果你刚好在sass之类的css预处理器,完全可以使用sass function来自动计算。 $rootSize: 16px!default;$designWidth: 750px!default;$designRatio: 2!default;@function rem($px) { @return $px / $rootSize / $designRatio * 1rem;}@function rootVw() { @return $rootSize / $designWidth * $designRatio * 100vmin;}这样就一次性计算好了根元素font-size,也可以使用rem()来自动计算rem了。 ...

June 12, 2019 · 1 min · jiezi

移动端适配资源整理

移动开发兼容问题整理笔记轻松掌握移动端web开发【尺寸适配】常用解决方案移动端页面适配———多方案解析如何在Vue项目中使用vw实现移动端适配

April 14, 2019 · 1 min · jiezi

第三代移动端布局方案

第三代移动端布局方案大家有没有发现淘宝的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

移动端适配方案 rem

有道笔记这个坑货????我的图片都400了!像素WT:ip5 — 640px1136px 则图片会平铺— 调试工具里面 显示320568px1.1 像素:px:css pixels 逻辑像素 浏览器使用的抽象单位 可以根据不同设备的关系变大变小dp,pt: device independent pixesls 设备无关像素 物理像素dpr: devicePixelRatio 设备像素缩放比 物理像素与逻辑像素的关系 与手机分辨率有关—-JS window.devicePixelRatio 获取到当前设备的dprCSS -webkit-device-pixel-ratio -webkit-min-device-pixel-ratio -webkit-max-device-pixel-ratio 进行媒体查询计算公式:1px = (dpr)2 dp长度上:1px = 2dp解释640px1136px —> 320568px iphone5的dpr = 2一个逻辑像素被四个物理像素去渲染1.2 为什么iphone5的dpr = 2 ?dpi:打印机每英寸打印的墨点数ppi: 屏幕每英寸的像素数量 ——–都是用来描述计算机屏幕的像素密度的根据物理像素计算ppippi越高,像素密度越高,图像越清晰ppi越低,可视度越低,系统默认设置的缩放比越小 – windows系统系统有默认缩放比,其与ppi有关eg: iphone5–>ppi:360 –> xhdpi –>dpr = 2总结各种单位关系:dpr不同 — 缩放不同,视觉效果不同 — 手淘,统一dpr大前端团队 > 移动端页面适配 > image2017-9-4 12:17:52.png我们已经解决了高清屏和普通屏的缩放问题,之后我们将解决不同屏幕尺寸的适配问题移动设备上的PC页面Q: 345px的窗口去查看1200px的页面1.viewport – 浏览器显示页面内容的屏幕区域 document.body.clientWidthlayout viewport 布局视口解决了早期的页面在手机上显示的问题,由于两者之间的宽度趋近,CSS只需要像在PC上那样渲染页面就行,原有的页面结构不会被破坏。在iOS Safari中定义了一个viewport meta标签,用来创建一个虚拟的布局视口(layout viewport),而这个视口的分辨率接近于PC显示器,所以pc上的网页基本能在手机上呈现,只不过元素看上去很小,一般默认可以通过手动缩放网页。visual viewport 视觉视口 可以通过scale来控制一个视口用来承载它,这个视口可以简单的认为是手持设备物理屏幕的可视区域,我们称之为(视觉视口)visual viewport,无法对它进行任何设置或者修改标准下的移动端meta设置:<meta name=“viewport” content=“width=device-width, initial-scale=1, maximum-scale=1”>为什么不使用默认的viewport = 980px 而是等于设备宽度scale:页面根据页面大小自动调整缩放比 = innerWidth / deviecWidth350px的页面 显示在 320px的 viewport中 缩放比为1.09<meta name=“viewport” content=“width=device-width,height=device-height,user-scalable=no,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,target-densitydpi=device-dpi” />content=“width=device-width 布局viewport = 设备宽度initial-scale=1 布局viewport = 度量viewportflex布局:常用场景兼容问题:ios. Android 4.4 flexAndroid<4.4 flexBox响应式设计—解决多终端处理核心:媒体查询常见参数与语法:语法:@media all{.box{background:red;}}/all 媒体类型and 关键字(min-width: 400px) 媒体特性 - 闭区间/相对单位px略显固定,不能根据尺寸的大小而改变,使用相对单位体验页面兼容性em - 父节点的font-size - 多层嵌套下难以维护rem - html的font-size - 可作为全局统一度量 1rem = html的font-size注意: font-size 不要使用rem字体大小是趋向于阅读的实用性而并不是排版布局同理:对于固定元素特性 – 我们不使用rem而是使用 px,确保在不同屏幕下 显示一致 eg:border常用文字适配处理–文字溢出:单行文本:.inaline{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}多行文本:.intwoline{display:-webkit-box !important;overflow:hidden;text-overflow:ellipsis;word-break:break-all;-webkit-box-orient:vertical;-webkit-line-clamp:2;}rem动态变化算法默认设计图大小 – 等分10份/75份 – 确定不同屏幕下一份的大小=1rem常规:var rem = width / 10;docEl.style.fontSize = rem + ‘px’;750px/10rem 1rem = 75px = html.font-size – 换算100px = 100/75rem640px/10rem 1rem = 64px = html.font-size优化:为了px与rem换算方便var winW = window.innerWidth;document.getElementsByTagName(“html”)[0].style.fontSize = winW * 0.15625 + “px”;—————————————————————————-= winW / 7.5 + “px”;整数 /10750px/75rem 1rem = 10px = html.font-size 100px =100/10rem原因:chrome font-size => 12px750px/7.5rem 1rem = 100px = html.font-size 100px =100/100rem根据设计图尺寸不同改变数值手淘 – flexiblehttp://blog.csdn.net/qq_16664… ...

March 14, 2019 · 1 min · jiezi

关于移动端1px的解决方法

使用伪类:after,:before和transform:scale()实现border-box{ position:relative;}.border-box:after{ content:’’; position:absolute; bottom:0; left:0; width:100%; border-top:1px solid #ddd; }@media (-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:2){ .border-box:after{ transform:scaleY(0.5); -webkit-transform:scaleY(0.5); } } @media (-webkit-min-device-pixel-ratio:3),(min-device-pixel-ratio:3){ .border-box:after{ transform:scaleY(0.3); -webkit-transform:scaleY(0.3); } }

March 13, 2019 · 1 min · jiezi

移动端尺寸基础知识

初涉移动端设计和开发的同学们,基本都会在尺寸问题上纠结好一阵子才能摸到头绪。我也花了很长时间才弄明白,感觉有必要写一篇足够通俗易懂的教程来帮助大家。从原理说起,理清关于尺寸的所有细节。由于是写给初学者的,所以不要嫌我啰嗦。现象首先说现象,大家都知道移动端设备屏幕尺寸非常多,碎片化严重。尤其是Android,你会听到很多种分辨率:480x800, 480x854, 540x960, 720x1280, 1080x1920,而且还有传说中的2K屏。近年来iPhone的碎片化也加剧了:640x960, 640x1136, 750x1334, 1242x2208。不要被这些尺寸吓倒。实际上大部分的app和移动端网页,在各种尺寸的屏幕上都能正常显示。说明尺寸的问题一定有解决方法,而且有规律可循。像素密度要知道,屏幕是由很多像素点组成的。之前提到那么多种分辨率,都是手机屏幕的实际像素尺寸。比如480x800的屏幕,就是由800行、480列的像素点组成的。每个点发出不同颜色的光,构成我们所看到的画面。而手机屏幕的物理尺寸,和像素尺寸是不成比例的。最典型的例子,iPhone 3gs的屏幕像素是320x480,iPhone 4s的屏幕像素是640x960。刚好两倍,然而两款手机都是3.5英寸的。所以,我们要引入最重要的一个概念:像素密度,也就是PPI(pixels per inch)。这项指标是连接数字世界与物理世界的桥梁。Pixels per inch,准确的说是每英寸的长度上排列的像素点数量。1英寸是一个固定长度,等于2.54厘米,大约是食指最末端那根指节的长度。像素密度越高,代表屏幕显示效果越精细。Retina屏比普通屏清晰很多,就是因为它的像素密度翻了一倍。倍率与逻辑像素再用iPhone 3gs和4s来举例。假设有个邮件列表界面,我们不妨按照PC端网页设计的思维来想象。3gs上大概只能显示4-5行,4s就能显示9-10行,而且每行会变得特别宽。但两款手机其实是一样大的。如果照这种方式显示,3gs上刚刚好的效果,在4s上就会小到根本看不清字。在现实中,这两者效果却是一样的。这是因为Retina屏幕把2x2个像素当1个像素使用。比如原本44像素高的顶部导航栏,在Retina屏上用了88个像素的高度来显示。导致界面元素都变成2倍大小,反而和3gs效果一样了。画质却更清晰。在以前,iOS应用的资源图片中,同一张图通常有两个尺寸。你会看到文件名有的带@2x字样,有的不带。其中不带@2x的用在普通屏上,带@2x的用在Retina屏上。只要图片准备好,iOS会自己判断用哪张,Android道理也一样。由此可以看出,苹果以普通屏为基准,给Retina屏定义了一个2倍的倍率(iPhone 6plus除外,它达到了3倍)。实际像素除以倍率,就得到逻辑像素尺寸。只要两个屏幕逻辑像素相同,它们的显示效果就是相同的。Android的解决方法类似,但更复杂一些。因为Android屏幕尺寸实在太多,分辨率高低跨度非常大,不像苹果只有那么几款固定设备、固定尺寸。所以Android把各种设备的像素密度划成了好几个范围区间,给不同范围的设备定义了不同的倍率,来保证显示效果相近。像素密度概念虽然重要,但用不着我们自己算,iOS与Android都帮我们算好了。如图所示,像素密度在120左右的屏幕归为ldpi,160左右的归为mdpi,以此类推。这样,所有的Android屏幕都找到了自己的位置,并赋予了相应的倍率:ldpi [0.75倍]mdpi [1倍]hdpi [1.5倍]xhdpi [2倍]xxhdpi [3倍]xxxhdpi [4倍]各型号iPhone的倍率比较简单,我们后面会讲到。那么Android手机那么多,具体怎么分?哪些手机是几倍的倍率呢?我们先看一张表,这是友盟2014年10月到2015年03月的数据:就目前市场状况而言,各种手机的分辨率可以这样粗略判断。虽然不全面,但至少在1年内都还有一定的参考意义:ldpi 如今已绝迹,不用考虑mdpi [320x480](市场份额不足5%,新手机不会有这种倍率,屏幕通常都特别小)hdpi [480x800、480x854、540x960](早年的低端机,屏幕在3.5英寸档位;如今的低端机,屏幕在4.7-5.0英寸档位)xhdpi [720x1280](早年的中端机,屏幕在4.7-5.0英寸档位;如今的中低端机,屏幕在5.0-5.5英寸档位)xxhdpi [1080x1920](早年的高端机,如今的中高端机,屏幕通常都在5.0英寸以上)xxxhdpi [1440x2560](极少数2K屏手机,比如Google Nexus 6)自然地,以1倍的mdpi作为基准。像素密度更高或者更低的设备,只需乘以相应的倍率,就能得到与基准倍率近似的显示效果。不过需要注意的是,Android设备的逻辑像素尺寸并不统一。比如两种常见的屏幕480x800和1080x1920,它们分别属于hdpi和xxhdpi。除以各自倍率1.5倍和3倍,得到逻辑像素为320x533和360x640。很显然,后者更宽更高,能显示更多内容。所以,即使有倍率的存在,各种Android设备的显示效果仍然无法做到完全一致。单位不难发现,真正决定显示效果的,是逻辑像素尺寸。为此,iOS和Android平台都定义了各自的逻辑像素单位。iOS的尺寸单位为pt,Android的尺寸单位为dp。说实话,两者其实是一回事。单位之间的换算关系随倍率变化:1倍:1pt=1dp=1px(mdpi、iPhone 3gs)1.5倍:1pt=1dp=1.5px(hdpi)2倍:1pt=1dp=2px(xhdpi、iPhone 4s/5/6)3倍:1pt=1dp=3px(xxhdpi、iPhone 6 plus)4倍:1pt=1dp=4px(xxxhdpi)单位决定了我们的思考方式。在设计和开发过程中,应该尽量使用逻辑像素尺寸来思考界面。设计Android应用时,有的设计师喜欢把画布设为1080x1920,有的喜欢设成720x1280。给出的界面元素尺寸就不统一了。Android的最小点击区域尺寸是48x48dp,这就意味着在xhdpi的设备上,按钮尺寸至少是96x96px。而在xxhdpi设备上,则是144x144px。无论画布设成多大,我们设计的是基准倍率的界面样式,而且开发人员需要的单位都是逻辑像素。所以为了保证准确高效的沟通,双方都需要以逻辑像素尺寸来描述和理解界面,无论是在标注图还是在日常沟通中。不要再说“底部标签栏的高度是96像素,我是按照xhdpi做的”这样的话了。Web怎么办移动端页面的绝对单位仍然是px,至少代码里这么写,但它的道理也和app一样。由于像素密度是设备本身的固有属性,它会影响到设备中的所有应用,包括浏览器。前端技术可以善加利用设备的像素密度,只需一行代码,浏览器便会使用app的显示方式来渲染页面。根据像素密度,按相应倍率缩放。可以通过这个测试页面 http://greenzorro.github.io/d… 来看看你的移动设备屏幕宽度,这是逻辑像素宽度。以iPhone 5s为例,屏幕的分辨率是640x1136,倍率是2。浏览器会认为屏幕的分辨率是320x568,仍然是基准倍率的尺寸。所以在制作页面时,只需要按照基准倍率来就行了。无论什么样的屏幕,倍率是多少,都按逻辑像素尺寸来设计和开发页面。只不过在准备资源图的时候,需要准备2倍大小的图,通过代码把它缩成1倍大小显示,才能保证清晰。实际应用大家最关心的还是实际运用,画布该怎么设置。我们就iOS、Android、Web三个平台来分别梳理一下。不过在这之前,我要为使用PS进行设计的朋友介绍一个小技巧。之前我说过,我们要以逻辑像素尺寸来思考界面。体现到设计过程中,就是要把单位设置成逻辑像素。打开PS的首选项——单位与标尺界面,把尺寸和文字单位都改成点(Point)。这里的点也就是pt,无论设计iOS、Android还是Web应用,单位都用它。当然,各平台单位名称还是要记住的。这里我们用的只是它的原理,不用在意名称。要调节倍率,则通过图像大小里的DPI来控制。这个DPI,其实就是PPI,像素密度。有个常识大家都知道,屏幕上的设计DPI设成72,印刷品设计DPI设成300。为什么是这两个数字?首先说300,这和人眼的分辨能力有关。由于1英寸是固定长度,每1英寸有多少个像素点决定了画质清晰程度。之前说过,这就是像素密度,也就是DPI。DPI达到300以上,其细腻程度就会给人真实感,像真实世界中的物件。相反,DPI只有10的话,在你一个食指指节大小的长度内只有10个像素,这明显就是马赛克了。所以印刷品要设成300,才能保证清晰。再说72,这有一定的历史原因。最早的图形设计是在mac电脑上进行的,mac本身的显示器分辨率就是72。PS中把图像DPI也设成72,就能保证屏幕上显示的尺寸和打印尺寸相同,便于设计。72的PC显示器分辨率逐渐成为一种默认的行业标准,这套规则就这么沿用下来。现在回到正题,我们怎么通过DPI来调节倍率?既然屏幕本身的分辨率是72,DPI设成72刚好是1倍尺寸,那设成72的两倍就是倍率为2的屏幕了,就这么简单。下面来看看3个平台各自的画布设置:iPhoneiPhone的屏幕尺寸各不相同,我说的是逻辑像素尺寸,这确实是让人很头疼的事情。如果想用一套设计涵盖所有iPhone,就要选择逻辑像素折中的机型。从市场占有率数据来看,目前最多的是iPhone5/5s的屏幕。倍率为2,逻辑像素320x568。上升势头最猛,未来有望登上第一的是iPhone 6的屏幕。倍率为2,逻辑像素375x667。按照这两种尺寸来设计,都是比较主流的做法。可以兼顾短一些的iPhone 4s,大一点的6 plus也不会过于空旷。不过在切图的时候要注意,由于iPhone 6 plus的3倍图是由2倍图放大而来,所以位图要注意保证清晰。Android都说Android碎片化严重,但它现在反而比iOS好处理。因为如今的Android屏幕逻辑像素已经趋于统一了:360x640,就看你设成几倍了。想以xhdpi为准,就把DPI设成72x2=144。想以xxhdpi为准,就把DPI设成72x3=216。对于那些比较老的低端机,宽度是480px的那批,画面确实会小一些,显示内容会更少。稍微留意一下,重要内容尽量保持在界面中上部分。当然,这些机型不出一年就会被边缘化,基本淘汰。现在能运转的也是当作功能机在用,软件多了必卡无疑,用户体验无从谈起。不作考虑也是OK的。Web手机端网页就没有统一标准了,比较流行的做法是按照iPhone 5的尺寸来设计。倍率2,逻辑像素320x568。这样的做法比较实在,倍率2的屏幕无论在iOS还是Android方面都是主流,而且又是2倍屏幕中逻辑像素最小的。所以图片的尺寸可以保持在较小的水平,页面加载速度快。当然,缺点就是在倍率3的设备上看,图片不是特别清晰。如果追求图片质量,愿意牺牲加载速度,那么可以按照最大的屏幕来设计。也就是iPhone 6 plus的尺寸,倍率3,逻辑像素414x736。总结移动端的尺寸比PC端复杂,关键就在倍率。但也正因为倍率的存在,把大大小小的屏幕拉回到同一水平线,得以保证一套设计适应各种屏幕。站在这条水平线的角度看,会发现它很好理解。

February 16, 2019 · 1 min · jiezi

移动端网页布局适配rem方案小结

前言根据 W3C 规范中对 1rem 的定义:1rem 与等于根元素 font-size 的计算值。当明确规定根元素的 font-size 时,rem 单位以该属性的初始值作参照。这就意味着 1rem 等于 html 元素的字体大小(大部分浏览器根元素的字体大小为16px)兼容性ios:6.1系统以上都支持android:2.1系统以上都支持大部分主流浏览器都支持,可以安心的往下看了。rem:(font size of the root element)意思就是根据网页的根元素来设置字体大小,和em(font size of the element)的区别是,em是根据其父元素的字体大小来设置,而rem是根据网页的跟元素(html)来设置字体大小的,举一个简单的例子,现在大部分浏览器IE9+,Firefox、Chrome、Safari、Opera ,如果我们不修改相关的字体配置,都是默认显示font-size是16px,即html { font-size:16px;} 那么如果我们想给一个P标签设置12px的字体大小,那么用rem来写就是p { font-size: 0.75rem; //12÷16=0.75(rem)}使用rem这个字体单位进行适配,就是利用它作为一个全局字体固定参照单位的特性。如果改变html元素的字体大小,rem的值也就跟着改变,对应的其他使用rem的布局尺寸,也会跟着改变,从而达到适配的目的,保证比例一致。 所以rem不仅可以适用于字体,同样可以用于width height margin这些样式的单位。rem适配具体实现方案:设计稿尺寸宽度为750px,如果设计稿是640px,下边js会自动计算rem的值(比如rem:75px -> rem: 64px),具体的尺寸rem不用调整(例如 padding: 1.5rem,不用调整,这是一个比例大小),对应的元素大小px值会根据新的rem(比如rem: 64px, padding等于 1.5 * 64)改变,从而按照比例适配。index.html<!DOCTYPE html><html lang=“en”> <head> <meta charset=“utf-8”> <meta http-equiv=“X-UA-Compatible” content=“IE=edge”> <title>rem适配</title> <script>;(function(win, lib) { var doc = win.document; var docEl = doc.documentElement; var metaEl = doc.querySelector(‘meta[name=“viewport”]’); var flexibleEl = doc.querySelector(‘meta[name=“flexible”]’); var dpr = 0; var scale = 0; var tid; var flexible = lib.flexible || (lib.flexible = {}); if (metaEl) { console.warn(‘将根据已有的meta标签来设置缩放比例’); var match = metaEl.getAttribute(‘content’).match(/initial-scale=([\d.]+)/); if (match) { scale = parseFloat(match[1]); dpr = parseInt(1 / scale); } } else if (flexibleEl) { var content = flexibleEl.getAttribute(‘content’); if (content) { var initialDpr = content.match(/initial-dpr=([\d.]+)/); var maximumDpr = content.match(/maximum-dpr=([\d.]+)/); if (initialDpr) { dpr = parseFloat(initialDpr[1]); scale = parseFloat((1 / dpr).toFixed(2)); } if (maximumDpr) { dpr = parseFloat(maximumDpr[1]); scale = parseFloat((1 / dpr).toFixed(2)); } } } 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; } docEl.setAttribute(‘data-dpr’, dpr); if (!metaEl) { metaEl = doc.createElement(‘meta’); metaEl.setAttribute(’name’, ‘viewport’); metaEl.setAttribute(‘content’, ‘initial-scale=’ + scale + ‘, maximum-scale=’ + scale + ‘, minimum-scale=’ + scale + ‘, user-scalable=no’); if (docEl.firstElementChild) { docEl.firstElementChild.appendChild(metaEl); } else { var wrap = doc.createElement(‘div’); wrap.appendChild(metaEl); doc.write(wrap.innerHTML); } } function refreshRem(){ var width = docEl.getBoundingClientRect().width; if (width / dpr > 540) { width = 540 * dpr; } var rem = width / 10; docEl.style.fontSize = rem + ‘px’; flexible.rem = win.rem = rem; } 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 = 12 * dpr + ‘px’; } else { doc.addEventListener(‘DOMContentLoaded’, function(e) { doc.body.style.fontSize = 12 * dpr + ‘px’; }, false); } refreshRem(); flexible.dpr = win.dpr = dpr; flexible.refreshRem = refreshRem; flexible.rem2px = function(d) { var val = parseFloat(d) * this.rem; if (typeof d === ‘string’ && d.match(/rem$/)) { val += ‘px’; } return val; } flexible.px2rem = function(d) { var val = parseFloat(d) / this.rem; if (typeof d === ‘string’ && d.match(/px$/)) { val += ‘rem’; } return val; } })(window, window[’lib’] || (window[’lib’] = {}));</script> </head> <body> <noscript> <strong>We’re sorry but rem适配 doesn’t work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id=“app”></div> <!– built files will be auto injected –> </body></html>helper.scss$remBase: 75;$primaryColor: #ffd633;@function px2rem($px) { @return ($px / $remBase) * 1rem;}%textOverflow { width: 100%; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;}// @include borderLineTop(’top’, color)@mixin borderLine($mode: ’top’, $color: #e5e5e5) { position: relative; @if $mode == ’top’ { &::before { // 实现1物理像素的下边框线 content: ‘’; position: absolute; z-index: 1; pointer-events: none; background-color: $color; height: 1px; left: 0; right: 0; top: 0; @media only screen and (-webkit-min-device-pixel-ratio: 2) { -webkit-transform: scaleY(0.5); -webkit-transform-origin: 50% 0%; } } } @if $mode == ‘bottom’ { &::after { // 实现1物理像素的下边框线 content: ‘’; position: absolute; z-index: 1; pointer-events: none; background-color: $color; height: 1px; left: 0; right: 0; bottom: 0; @media only screen and (-webkit-min-device-pixel-ratio: 2) { -webkit-transform: scaleY(0.5); -webkit-transform-origin: 50% 0%; } } }}@mixin borderRadius($radius) { border-top-left-radius: px2rem($radius); border-top-right-radius: px2rem($radius); border-bottom-left-radius: px2rem($radius); border-bottom-right-radius: px2rem($radius);}// @include banner(100)@mixin banner($height) { position: relative; padding-top: percentage($height/750); // 使用padding-top height: 0; overflow: hidden; img { width: 100%; height: auto; position: absolute; left: 0; top: 0; }}$spaceamounts: (5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 100);$sides: (top, bottom, left, right);@each $space in $spaceamounts { @each $side in $sides { .m-#{str-slice($side, 0, 1)}-#{$space} { margin-#{$side}: #{px2rem($space)} !important; } .p-#{str-slice($side, 0, 1)}-#{$space} { padding-#{$side}: #{px2rem($space)} !important; } }}.flex-center { display: flex; align-items: center;}@mixin font-dpr($font-size){ font-size: $font-size; [data-dpr=“2”] & { font-size: $font-size * 2; } [data-dpr=“3”] & { font-size: $font-size * 3; }}App.vue, 使用px2rem进行转换<style lang=“scss”>@import “@/assets/style/helper.scss”;#nav { padding: px2rem(24); a { font-size: px2rem(24); font-weight: bold; color: #2c3e50; &.router-link-exact-active { color: #42b983; } }}</style> ...

January 28, 2019 · 4 min · jiezi

移动端适配之二:visual viewport、layout viewport和ideal viewport介绍

上一篇博文,可算把像素这个东西讲清楚了。在这篇博文里面,将继续介绍viewport相关的内容。很多博客都会提到PPK所讲的三个viewport,有的讲的比较复杂,看的云里雾里,我这里也大概介绍一下,三个viewport主要是相对于移动端而言的。visual viewport这个是浏览器给我们用的、能真正用来显示网页内容的区域,可以通过下面的js命令获取:window.innerWidthwindow.innerHeight正如上篇博客所说的,前端里面能获取到的像素基本上都是CSS像素,所以这个的单位也是CSS像素。对于iPhone X,浏览器全屏状态下,其window.innerWidth的值为375。上篇博客中还提到screen.width和screen.height,主要是用来获取整个屏幕的大小的,而window.innerWidth和window.innerHeight只是获取浏览器可用显示区域的大小,也就是浏览器中间负责显示的部分。当浏览器全屏时,要去掉状态栏、标签栏、任务栏等区域,当浏览器非全屏时,其值更小。由于在移动端,浏览器一般都是全屏的,所以大多数情况下screen.width与window.innerWidth的值相等,也有的博客中说用screen.width和screen.height来获取visual viewport的大小,就是这个原因。visual viewport是我们可以直观看到的,不严谨的说,就是差不多等于手机屏幕的大小,偏向于一个物理概念。layout viewport网页最早是出现在电脑上的,上一篇博客中提到,电脑的物理像素可能比手机还要低,但是电脑的设备无关像素(或者说是分辨率吧,更严谨一些)是明显大于手机的设备无关像素的,毕竟电脑的屏幕尺寸远比手机大啊。那些在电脑上的网页,如果没有经过专门的优化,直接搬到手机上看,那么问题就来了,网页会被挤得变形,相信这种问题大家都遇到过。所以呢,手机厂商为了解决这个问题,设置了一个layout viewport。这是一个虚拟的窗口,其大小比手机屏幕大,加载网页时,直接把HTML渲染在这个虚拟的窗口中,这样就不会样式错乱了。在查看的时候,毕竟手机的visual viewport小啊,那就只能通过滚动条来看了。做个比喻,layout viewport就是一张大白纸,HTML的内容就写在这个大白纸上,visual viewport就是一个放大镜,上下左右移动,可以显示其中的一部分。Layout viewport的大小可以通过document.documentElement.clientWidth和document.document.clientHeight,实际使用中可能会有一些兼容问题,这跟DOCTYPE声明有关。不同浏览器的layout viewport大小不同,常见的有980px、1024px。ideal viewportLayout viewport是为了能将电脑上的网页正确的显示到手机上。当浏览器拿到一个网页时,首先会渲染到这个layout viewport里面。可是现在有很多网页会针对手机做专门的设计,比如现在的一些H5活动页,设计的尺寸就是在手机上看的。此时如果还是把网页渲染到这个大的layout viewport上,实在是有点不合适了。所以,还应该有个ideal viewport,这个ideal viewport应该与手机屏幕大小的相同,确切来说,等于visual viewport的大小。把页面渲染到这个ideal viewport里面,就能在visual viewport中完美显示。小结根据我的理解小结一下:layout viewport和ideal viewport都是用来渲染页面,layout viewport较大,用来渲染电脑上的页面,ideal viewport较小,用来渲染专门针对手机设计的页面;而visual viewport是用来查看layout viewport和ideal viewport的,是用来查看渲染的结果的。visual viewport是很具体的,而layout viewport和ideal viewport是比较抽象的。某种程度来说,layout viewport和ideal viewport可以理解成是两种尺寸,承载页面渲染的盒子,可以设置成layout viewport的尺寸,也可以设置成ideal viewport的尺寸,而且在默认情况下是layout viewport的尺寸。如果我们设置HTML中body为width:100%,那么这个body的实际宽度,将由这个盒子的宽度决定。在下一篇博文中,将介绍如何用meta标签来设置viewport,也就是设置这个承载HTML页面渲染的盒子的尺寸,从而达到最佳的显示效果。

December 27, 2018 · 1 min · jiezi