|
移动端H5页面布局适配总结
移动端H5页面布局适配总结
曾维军@贝壳找房
贝壳产品技术
贝壳产品技术 “贝壳产品技术公众号”作为贝壳官方产品技术号,致力打造贝壳产品、技术干货分享平台,面向互联网/O2O开发/产品从业者,每周推送优质产品技术文章、技术沙龙活动及招聘信息等。欢迎大家关注我们。 242篇内容
2020年12月19日 23:47
说起移动端的H5页面布局适配解决方案,从移动端被重视起到今天,前前后后发展演变出各种各样的方案,其中不乏简单粗暴的手段,也有一些发展到今天已经成为比较普适的”真香“方案 。主要方案有:360px 方案利用viewport自动调节scale方案360px 页面整体缩放方案百分比计算方案rem 计算方案Vw,vh方案媒介查询media弹性盒方案下面将重点介绍其中几种方案。一百分比计算方案viewport 的设置基本如下(篇幅有限,关于横向的 meta 设置就不过多阐述):原理:使用百分比来设置宽度以及布局,高度视情况而定,例如:UI 设置的 360px 图里面有一个 100px 的区域,这个时候该区域宽度设置:目标width=(100/360)*100%而且一般的情况下,我们都会用 max-width 和 min-width 做相关的宽度限制以防止过度的拉伸。以下是子元素设置百分比的参照值:子元素参照值Width/height基于子元素的直接父元素top/bottom 和 left/right相对于直接非static定位的父元素的height/widthpadding/margin不论是垂直方向或者是水平方向,都相对于直接父亲元素的width,与父元素的height无关border-radius相对于自身的宽度优势:兼容性好,且掌握好参照值之后,在一定范围内基本不会出现适配的问题劣势:开发过程中计算量大,且参照系容易选错,当屏幕跨度超过设计稿太多时,显示相较于原来设计稿会出现比较大的变形,并且如果不注意垂直方向的适配计算的话,容易出现长宽度与设计稿不一致,另外,background-position参照于背景图片的像素, IE怪异模式对盒模型的解析不同等也是会影响参照值;垂直方向的适配问题 !!!因为市面上的手机宽度比并不是一定的,千万别按照宽度来计算高度的百分比,而是应该按照高度来计算,其他参照参考以上表格二rem 计算方案(推荐)原理:rem是相对长度单位, 通过获取屏幕实际宽度动态的设置 html 的字体大小,1rem = html 的字体大小像素(font-size)。以下贴出一个简单的 REM 的计算方案:(function(doc,win){letdocEl=doc.documentElement,//获取htmlresizeEvt="orientationchange"inwindow"orientationchange":"resize",width=375,//设计稿宽,用时只需要修改这一处recalc=function(){constclientWidth=docEl.clientWidth;//获取设备尺寸if(!clientWidth)return;//如果没有值,回去if(clientWidth>width){//如果超过设计稿宽度,就给一个固定值docEl.style.fontSize="100px";docEl.style.width=width+"px";docEl.style.margin="0auto";}else{docEl.style.fontSize=100*(clientWidth/width)+"px";}};if(!doc.addEventListener)return;//如果没有这个方法,回去win.addEventListener(resizeEvt,recalc,false);//改变大小时调整一下doc.addEventListener("DOMContentLoaded",recalc,false);//加载完成时调整})(document,window);//使用时:1rem =设计稿的宽度/ 100实际上以上就是最基本的 rem 的实现原理,当然像淘宝那些 flexible.js 原理基本都是这样,只不过兼容性,边界条件处理得更加完备优势:使用简单,兼容性好,IOS, Andriod 以及大部分的主流浏览器都支持,开发计算量小,参照系单一劣势:需要 JS 来配合实现动态改变字体大小来适配:因此,设置字体的 JS 如果在 css 字后则会出现一开始闪一下大小,然后再适配的大小。使用需要保证该段 JS 在 css 之前。小数点的像素问题:rem 通过动态屏幕宽度计算下来可能会出现小数点像素,浏览器渲染会对这部分小数进行四舍五入处理,而现实则会按照真实现实,简而言之就是渲染的大小和实际真实显示的大小可能不一致。举个例子:0.85px 渲染按照 1px渲染,但是实际显示还是按照原来的 0.85px显示,那么就会空出来 0.15px 被临近的元素占据 ,另外 低于0.5px 又会被渲染成 0px,而实际则不是 0 会占据临近元素的空间,这就导致:缩放到低于1px的元素时隐时现(tips: 指定最小的转化像素可以解决)还有其他的一些因为小数像素计算导致的误差:line-height 不能完全垂直居中,border-radius,当然,一般情况指定了最小转化像素之后这些细微的误差是可以接受的。最佳推荐:利用弹性盒布局的居中方案可以完美解决该问题cursor: pointer 元素点击背景变色的问题一些类活动页面垂直方向的适配问题Tips: 垂直方向的适配, 在一些图片素材比较多的活动页面一定要注意,垂直方向适当的在间隙,空白区域使用百分比单位或者 vh, 因为手机长宽比不一致,而有些活动页又需要一屏不出现滚动条,这个时候应该保证具体元素等比缩放(不变形)来用百分比或者 vh 大得布局,间隔实现适当调整来保证内容的均匀分布于良好的体验。rem的相关工具包px2rem的介绍上面介绍了rem的一些基本原理相关知识,那么这里再给大家带来一个vue的rem成熟的使用方案,当然还有其他各种,具体选择根据场景与需求做出相应选择即可。1. 安装npminstallpx2rem-loaderlib-flexible–save2. 在项目入口文件main.js中引入lib-flexibleimport‘lib-flexible/flexible.js’3. 在vue-cli生成的文件中,找到以下文件 build/utils.js,如下图添加配置'usestrict'constpath=require('path')constconfig=require('../config')constExtractTextPlugin=require('extract-text-webpack-plugin')constpackageConfig=require('../package.json')exports.assetsPath=function(_path){constassetsSubDirectory=process.env.NODE_ENV==='production'config.build.assetsSubDirectory:config.dev.assetsSubDirectoryreturnpath.posix.join(assetsSubDirectory,_path)}exports.cssLoaders=function(options){options=options||{}constcssLoader={loader:'css-loader',options:{sourceMapptions.sourceMap,importLoaders:5//在css-loader前应用的loader的数目,默认为0.//如果不加这个@import的外部css文件将不能正常转换//如果不行请试着调大数字//更改后必须调大数字否则无效}}constpx2remLoader={loader:'px2rem-loader',options:{remUnit:75//设计稿的1/10,我们假设设计稿是750px}}constpostcssLoader={loader:'postcss-loader',options:{sourceMapptions.sourceMap}}//generateloaderstringtobeusedwithextracttextpluginfunctiongenerateLoaders(loader,loaderOptions){constloaders=options.usePostCSS[cssLoader,postcssLoader]:[cssLoader,px2remLoader]//添加px2remLoaderif(loader){loaders.push({loader:loader+'-loader',options:Object.assign({},loaderOptions,{sourceMapptions.sourceMap})})}//ExtractCSSwhenthatoptionisspecified//(whichisthecaseduringproductionbuild)if(options.extract){returnExtractTextPlugin.extract({use:loaders,fallback:'vue-style-loader'})}else{return['vue-style-loader'].concat(loaders)}}//https://vue-loader.vuejs.org/en/configurations/extract-css.htmlreturn{css:generateLoaders(),postcss:generateLoaders(),less:generateLoaders('less'),sass:generateLoaders('sass',{indentedSyntax:true}),scss:generateLoaders('sass'),stylus:generateLoaders('stylus'),styl:generateLoaders('stylus')}}//Generateloadersforstandalonestylefiles(outsideof.vue)exports.styleLoaders=function(options){constoutput=[]constloaders=exports.cssLoaders(options)for(constextensioninloaders){constloader=loaders[extension]output.push({test:newRegExp('\\.'+extension+'
),use:loader})}returnoutput}exports.createNotifierCallback=()=>{constnotifier=require('node-notifier')return(severity,errors)=>{if(severity!=='error')returnconsterror=errors[0]constfilename=error.file&error.file.split('!').pop()notifier.notify({title:packageConfig.name,message:severity+':'+error.name,subtitle:filename||'',icon:path.join(__dirname,'logo.png')})}}这样我们就能在写css的时候基本自由的使用设计稿的Px了 px2rem loader会帮我们做转化。注意:1)不能在index.html的头部加 name 为 viewport 的 meta 标签,flexible会自动为我们添加2)对于外部引入的css文件,有时候px2rem能正常转换,有时候又不能转换,到底是什么原因呢?试验了三种不同的css引入情况,现在给出一种能正常转换的情况:三vh/vw 方案(推荐)原理:Vh,vw主要是 css3 中新出的为了进行移动适配的长度单位:主要是相对于视口viewport的百分比1vw = 视口宽度 1%1vh = 视口高度 1%vmin = vw 和 vh 中最小的那个Vmax = vw 和 vh 中最大的那个计算方式实际上与百分基本一致,不过多了一个优势,就是参照系没有百分比那么复杂$vw_base:375@functionvw($px){@return($px/375)*100vw;}//使用css预处理器lessscss都可以支持优势:原理简单,参照系单一,相比于百分比更加不易出错纯 css 解决方案,相比于 rem 更有解耦性劣势:只支持 CSS3 的浏览器鉴于以上,vw,vh 的优势 目前也已经成为移动比较主流的适配方案之一,对于其兼容性问题,基本上移动端包括 H5 发展至今,也基本可以考虑忽略不计了,因为一些不支持的 IE 基本也已经被市场淘汰,特别是在移动端。~~~真香四媒介查询曾几何时,随着 H5 和 CSS3 被业界炒的火热的时候,基于媒介查询(与视口 viewport 相关)的响应式布局一度被认为是未来的发展趋势,本人也尝试在实际项目中开发过响应式布局的项目,到最后实际上项目体验并不是特别的美好,具体原因会在下面给大家分析。原理:结合媒介查询mediascreenand(max-width:320px){// screen 主要有三个参数:1.显示的媒介(screen屏幕等)//2.最大宽度3.最小宽度.ads{display:none;}}@mediascreenand(min-width:320px)and(max-width:750px){.ads{display:none;}}这样就能在不同的媒介下例如屏幕,其他显示设备上,按照设备,以及宽度将将元素展示成不同样式来达到响应与适配的目的,其中微软的官网是业界使用响应式比较早之一,感兴趣的同学可以去研究研究。优势:不同屏幕与设备精确适配达到最好的用户友好度对一些布局紧凑的页面 and 活动页的适配是最友好的方式,没有之一PC,移动只需要一套代码劣势:需要匹配多套设备与区间进行匹配,开发工作量从这方面讲会多出不少实际上存在一定的“冗余”代码最致命的还是由于PC和移动的交互方式和用户行为存在差距,不仅仅需要CSS适配,连一下交互方式也可能需要俩套JS来适配。五弹性盒布局(强烈推荐)随着 HTML5 和 CSS3 给我们带来了弹性盒布局,移动端开发的很多问题基本也变得 so easy, 你的 UE,UI 走查再也不用担心你的大的布局的适配问题了,对于弹性盒布局,最后出现display: box; 慢慢发展到各家厂商 支持的其他有flexbox, flex, 目前支持度最好的是flex, 但是我们如果需要做完美兼容基本按照以下写法就已经 OK 了:.flex{ display:-moz-box;/*Firefox*/display:-ms-flexbox;/*IE10*/display:-webkit-box;/*Safari*/display:-webkit-flex;/*Chrome,WebKit*/display:box;display:flexbox;display:flex; }在需要的 css 预处理器中使用mixins即可, 现在随着弹性盒支持度的发展基本已经成为标准,我们的 webpack 的 css-loader 也会在你开发时候只写display: flex即可,这些 loader 可以自动帮你完成弹性盒的兼容处理,使用起来很方便,而且现在基本本人开发中,关于布局问题,均匀分布,之前的经典的圣杯布局,双飞翼布局等等,使用弹性盒布局起来就方便很多,篇幅有限,仅仅贴出一个圣杯布局对比:效果图:(俩边宽度固定,中间宽度自适应)//弹性盒.main{display:flex;}.box1{width:100px;background:green;}.box2{flex:1;background:red;}.box3{width:100px;background:gray;}box1box2box3//传统圣杯布局(浮动加margin实现).box{float:left;}.left{width:100px;background:green;margin-left:-100%;}.middle{width:100%;background:red;}.right{width:100px;background:gray;margin-left:-100px;}.content{margin-left:100px;margin-right:100px;}middleleftright仔细观察,不难发现,弹性盒布局明显更加清晰,需要设置的也更少固定的子元素设置固定宽度,其他的按照相应的比例设置 flex 的值即可,传统的实现方式,html 的结构顺序也非显示的顺序。以下给大家介绍一下关于弹性盒布局的一些属性:1)容器属性2)项目属性3) order 相关的属性:flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大flex-shrink属性定义了项目的缩小比例,默认为 1,即如果空间不足,该项目将缩小。align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。Tips: flex 更详尽的属性 flex-direction,flex wrap 等介绍详见文档:https://www.w3cplus.com/css3/a-guide-to-flexbox.html优势:纯粹的 CSS+html 解决方案,父元素 display:flex,子元素配合设置布局,排列方向,排列方式,分布方式等即可html 的结构清晰。需要更少的层级特别是在移动端,良好的align与justify居中可以完美替换以前还需要:定位加 margin, margin: auto 等各种复杂的居中解决方案劣势:如果非要我列举劣势的话,emmmm, 我只想说还在使用老版本的三星啊,华为的,oppe 的瓶友们整点钱把手机换了吧. 你这些手机千万别卖。再收几年,可以当收藏品了4) 使用过程中的一些问题:作为一个新的属性,弹性盒在使用过程也或多或少在某些机型会存在一些小的问题大概罗列一下:当设置 display: -webkit-box 时,子元素宽度设置 -webkit-box-flex: 1;,当内容较多的时间可以可能会导致出现比例失效,display: flex 则不会出现该问题(解决方案:子元素设置的宽度为 0 即可解决比例失效问题),代码如下图:.box{display:-webkit-box;-webkit-box-orient:horizontal;width:600px;}.left{width:0px;//解决当子元素内容过多导致比例失效-webkit-box-flex:1;background:red;}display:flex 布局时候会出现子元素宽度设置失效问题://解决方案flex-grow:0;flex-shrink:0;flex-basis:auto;极少数老版本的浏览器会有兼容问题,几乎可以忽略六360px 利用 viewport 浏览器自动调节视口 scale 缩放的方案该种方案主要是指:UI 设计师出的 pad 素材基本也是按照 360px 进行设计的,那么我们前端工程师在切页面(开发)过程中就只需要按照给定的比例按照 px 来进行布局开发即可,那么我们的网页就会按照手机屏幕自动的缩放,并且不会出现不适配的问题。那么,估计有的小伙伴就会疑惑了,就算那个时候设备像素再低,但是实际上手机的像素与视口(viewport)大小并不一致,怎么就会出现这么神奇的事情呢?实际上原因也很简单:没错就是你的每个 html 文件里面几乎都会有的 meta name 为 viewport 的关于视口(“窗口”)的这个标签要实现这个其实很简单:即:简单粗暴的将视口设置成 360px, 并且不设置 initial-scale,max-scale 这些缩放设置,这个时候手机浏览器会根据手机屏幕大小来调节 scale 的比例来达到与视口适配:设置宽度 w * 手机自动缩放的比例 R = 手机实际的像素宽度 W (非物理像素)优势:在早期的移动端开发中,由于移动端设备本身的像素比较低,因此采用该种开发解决方案其实还是比较“省力” 简单的方式,避免了类似百分比 rem等计算的复杂性,兼容性比较好,因此一些外包公司甚至一些小厂都会采用该种方案。劣势:当手机像素越来越来越大的时候,因为这个时候是通过缩放页面而非(像 REM 方案那样)设置更高的像素来实现的适配,因此就会造成性能浪费。如果至今你还采用这种方案的话,广告词都帮你想好了:我们比 iphone 12 更环保,因为我们让全世界手机手机屏幕分辨率降低了 N 个 level, lower power,lower cost!!!七360px 标准开发,transform: scale来实现页面整体缩放原理很简单:页面 Body 以及外层容器都按照 360px 的尺寸开发,然后再加载页面的时候获取一下移动设备的屏幕宽度,然后计算出与 360px 的比例即:transform: scale(R) 这个 R 为 手机屏幕宽度/360, 这样的做法实际上与第一种方法都会造成手机的性能浪费:因此也只会在早期的一些小厂,和一些小的框架里有采用附上转化原理源码:functionresizeWidth(){window.location.reload()if($(window).width()>=360){varratio=$(window).width()/($(window).width()||$('body').width());$(window).height()&$('body').css('height',$(window).height());}else{$('body').css('height',"667px");}varratio=$(window).width()/(360||$('body').width());$('body').css({transform:"scale("+ratio+")",transformOrigin:"lefttop",backgroundSize:"100%"});}tips: 注意这里这个 360 实际上是可以根据后续手机发展设置更大一点,但实际终究不算一个很好的解决方案优势:基本与前一方案差不多,开发简单,无需复杂计算。兼容性比较好劣势:基本同上一方案,性能浪费八总结与实际使用体验:首先需要明确一点的是:并不存在某一个方案完美适用于移动端的所有场景,也并不是每一个方案到现在已经毫无用处。后面的实际使用推荐就会说明这一点首推的布局方案为:弹性盒布局(如果实在不嫌麻烦大的分块布局使用vw,vh也可以,甚至使用 rem 也行)但是不太推荐使用:百分比或者以前的 360px 缩放页面那方案接下来在给出我自己的解决方案之前,先看一个对比的动画图:估计一些有经验的 FE 同学可能已经看出来了,前者可能大概就是使用的 rem.或者 vw,vh 之类的方案,yes, 你猜得没错,但是感觉貌似体验也不是特别好,那为什么后面一个看起来体验好很多:原因很简单就是我采用了复合方案。前者 纯粹的rem, vw/vh 都几乎会是这样的效果,在屏幕小到一定程度的时候, 并且内容比较密集的时候字体和小间隙的适配就会成为一个问题, 那么怎么办呢?解决方案(本人推荐):1. rem、vw/vh、百分比以及弹性盒做整体的布局:- 当内容密集时考虑利用 媒介查询 来针对密集区域的内容来实现不同宽度的类似icon、字体、margin,padding 小边距的设置(px)这样就不会因为适配而使得间距变小甚至挤到一起这种不太好的体验,实际上现在淘宝、京东这些都是这套解决方案- 当内容不密集时就可以使用 rem, vw,vh 来设置 px 来设置即可2. 对于一些 UI 场景设计出来小于浏览器设置的12px像素九其他通用问题:9.1 1px 像素问题:1px,就是指 1 CSS 像素。但是明明 CSS 写的是 1px 但是到了有的手机上就会看起很粗?原因其实也很简单,是因为手机的dpr 不一样,至于 dpr 简单的说就是多少个物理像素来显示一个 CSS 像素点的概念:DPR = 设备像素 / CSS像素(某一方向上)9.1.1 方案一:媒介查询+transform: scale 缩放来实现@mediaonlyscreenand(-webkit-min-device-pixel-ratio:2){.line:before{-webkit-transform:scaleY(0.5);transform:scaleY(0.5);-webkit-transform:scaleY(0.5);-moz-transform:scaleY(0.5);-o-transform:scaleY(0.5);}}@mediaonlyscreenand(-webkit-min-device-pixel-ratio:3){.line:before{-webkit-transform:scaleY(0.33);transform:scaleY(0.33);-webkit-transform:scaleY(0.33);-moz-transform:scaleY(0.33);-o-transform:scaleY(0.33);}}9.1.2 方案二以及原理:假设有一天出了 iphone66 drp 是 8, 那么相当于水平是 8*8 个物理像素来显示一个 CSS 像素点,那么我们是不是实际上应该设置成 1/8px 就可以解决,那么缩放就是解决方法之一。通过:将 scale 设置为 1/dpr9.1.3 通过获取设备 DPR 来动态设置字体的大小也可以解决该问题//获取屏幕宽度dpr值constdeviceWidth=document.documentElement.clientWidth,dpr=window.devicePixelRatio||1;//设置根字体扩大dpr倍//由于deviceWidth当页面缩小dpr倍时,本身获取的值就增加dpr倍//所以这里不需要再乘以dpr了document.documentElement.style.fontSize=deviceWidth+"px";//设置页面缩放dpr倍document.getElementsByName("viewport")[0].setAttribute("content","width=device-width;initial-scale="+1/dpr);9.2 关于图片的异步加载问题早期浮动布局使用较多的情况,网络不稳定,经常会出现某一个图片加载不出来导致整个页面布局一下崩溃的问题,比较常用的解决方案有:采用占位的思路数据库存储图片宽高,提前动态设置弹性盒布局,这样就算没对某一图片加载失败的边界情况做兼容处理,也不会导致大面积布局混乱9.3 高清图片以及高清背景等问题9.3.1Img dpr 动态引入使用js创建image对象之后,根据dpr以及业务需求动态加载不同分辨率的图片9.3.2 在 MDN 上高清图片的懒加载MDN 浏览器兼容性列表里显示,除了果子全家和 IE,以及某些低版本的浏览器外,目前高版本的浏览器已经开始支持一个属性:lazyloadloadingIndicates how the browser should load the image:eageroads the image immediately, regardless of whether or not the image is currently within the visible viewport (this is the default value).lazyefers loading the image until it reaches a calculated distance from the viewport, as defined by the browser. The intent is to avoid the network and storage bandwidth needed to handle the image until it’s reasonably certain that it will be needed. This generally improves the performance of the content in most typical use cases.9.4 移动端 IOS 和 andriod 关于 input 这些标签表现不一致的适配问题现在移动端基本都是 vant 类似这个 UI 框架,基本上已经帮忙解决了这部分的适配问题,但是实际上我们还是要知道有什么问题以及是怎么实现的,比如前几天项目中使用 mint-ui 中radio 就存在单选选中多个以及不灵敏的情况,只得自己写一个组件来解决该问题:利用去掉原来默认样式+伪类或者其他方式实现,贴代码:{{radio.label}}9.5 一些其他的问题平时在移动端开发需要多注意的问题:移动端键盘被顶起的问题点击事件穿透的问题(冒泡机制等造成的)click 事件天生延迟的问题video 标签的播放条层控制,自动播放的问题中英文字数获取不一致的问题等等默认字体设置的问题特殊字体处理等等最后,特别感谢林静老师对本文的指导意见,也欢迎读者朋友留言指正,共同成长,不胜感激!参考:flex:http://www.ruanyifeng.com/blog/2018/10/flexbox-form.html关于 viewport:https://www.cnblogs.com/2050/p/3877280.html
预览时标签不可点
FE33大前端69FE · 目录#FE上一篇kemis中的数据处理下一篇基于沉淀场景的开发模式-原石关闭更多小程序广告搜索「undefined」网络结果
|
|