移动端适配解决方案
优化以前写过的一篇文章:https://www.cnblogs.com/beileixinqing/p/11283315.html
目前常见移动端适配方案
1、媒体查询
- 通过写媒体查询,在不同的分辨率下写对应不同的样式,这样带来以下几点缺点:
- 开发上的繁琐,需要针对不同设备下写对应适配的样式;
- 在不同设备或者不同分辨率切换下,效果变化时的视觉冲击,带来不好的用户体验;
2、通过 rem 单位来实现适配
通过设置根元素的font-size,其他单位使用rem,但是需要引入一段js,动态判断当前设备的dpr,从而改变根元素的font-size,也是一种很好的方案,例如 著名的flexible.js,非常好用,唯一不足时多引入了一个js文件,而且对安卓的适配性不是很好。
3、通过视口单位( Viewport units )
在业界,极为推崇的一种理论是 Peter-Paul Koch (江湖人称“PPK大神”)提出的关于视口的解释——在桌面端,视口指的是在桌面端,指的是浏览器的可视区域;而在移动端较为复杂,它涉及到三个视口:分别是 Layout Viewport(布局视口)、 Visual Viewport(视觉视口)、Ideal Viewport。
而视口单位中的“视口”,在桌面端,毫无疑问指的就是浏览器的可视区域;但是在移动端,它指的则是三个 Viewport 中的 Layout Viewport 。
根据CSS3规范,视口单位主要包括以下4个:
- vw : 1vw 等于视口宽度的1%
- vh : 1vh 等于视口高度的1%
- vmin : 选取 vw 和 vh 中最小的那个
- vmax : 选取 vw 和 vh 中最大的那个
视口单位区别于%
单位,视口单位是依赖于视口的尺寸,根据视口尺寸的百分比来定义的;而%
单位则是依赖于元素的祖先元素。
用视口单位度量,视口宽度为100vw,高度为100vh(左侧为竖屏情况,右侧为横屏情况)
例如,在桌面端浏览器视口尺寸为650px,那么 1vw = 650 * 1% = 6.5px(这是理论推算的出,如果浏览器不支持0.5px,那么实际渲染结果可能是7px)。
兼容性
其兼容性如下图所示,可以知道:在移动端 iOS 8 以上以及 Android 4.4 以上获得支持,并且在微信 x5 内核中也得到完美的全面支持。
截图来自Can I Use
截图来自X5内核-Can I Use
视口单位特点是宽度或者高度不同,对应的尺寸也会发生变化,从而达到适配即自适应,然而也有缺点,不能设置最大最小宽度限制,当比如横屏展示的时候,宽度很宽的情况下,必须设置最大font-size限制,防止字体显示过大;
4、视口单位+rem布局
- 给根元素大小设置随着视口变化而变化的 vw 单位,这样就可以实现动态改变其大小。
- 限制根元素字体大小的最大最小值,配合 body 加上最大宽度和最小宽度
自己常用的两种方案
方案一:使用lib-flexible包
参考链接:https://www.w3cplus.com/mobile/lib-flexible-for-html5-layout.html
使用flexible包方式,安装 lib-flexible 包和 px2rem-loader包
npm install --save-dev lib-flexible px2rem-loader |
---|
在需要的js文件中头部引入,如果是vue项目就引入到main.js中:
import 'lib-flexible'
webpack配置loader,注意顺序很重要,顺序不对会出错
{ test: /.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', { loader: 'px2rem-loader', options: { remUni: 75, remPrecision: 8, } }, { loader: 'postcss-loader', options: { plugins: [require("autoprefixer")("last 100 versions")] } }] }, { test: /.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader?importLoaders=1', { loader: 'px2rem-loader', options: { remUni: 75, remPrecision: 8, } }, { loader: 'postcss-loader', options: { plugins: [require("autoprefixer")("last 100 versions")] } }, 'less-loader', ] }, |
---|
这里有个问题,在安卓下flexible.js源码是全部按dpr=1来适配的,那自然是不行的,我们修改一下源码,改为按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 = devicePixelRatio; //这里将原来=1改为devicePixelRatio } |
---|
然后写针对不同dpr下字体大小的适配,这里用less实现:
.font-dpr(@font-size) { font-size: @font-size; [data-dpr="1"] & { font-size: @font-size; } [data-dpr="2"] & { font-size: @font-size * 2; } //for mx3 [data-dpr="2.5"] & { font-size: @font-size * 2; } //for 小米note,for 小米mix [data-dpr="2.75"] & { font-size: @font-size * 2.2; } [data-dpr="3"] & { font-size: @font-size * 2.2; } //for 三星note4 ,三星s6 [data-dpr="4"] & { font-size: @font-size * 2; } } |
---|
使用的时候直接.font-dpr(20) 就可以了。
方案二:使用less或者sass等CSS 预处理语言写适配方案
参考链接:https://juejin.im/post/5caaa230e51d452b672f9703#heading-7
基准按照设计图尺寸,进行vw,vh换算,设置html元素字体大小。
这里贴出我的mixin.less
@baseDesign: 375; // 设计图尺寸默认按宽为375 @baseSize: @baseDesign/10; // 设计图尺寸默认按宽为375 / 10 .font-size(@px) { font-size: (@px/@baseSize/2)*1rem; } .margin(@px) { margin: (@px/@baseSize/2)*1rem; } .margin-all(@a,@b,@c,@d) { margin: (@a/@baseSize/2)*1rem (@b/@baseSize/2)*1rem (@c/@baseSize/2)*1rem (@d/@baseSize/2)*1rem; } .padding(@px) { padding: (@px/@baseSize/2)*1rem; } .padding-all(@a,@b,@c,@d) { padding: (@a/@baseSize/2)*1rem (@b/@baseSize/2)*1rem (@c/@baseSize/2)*1rem (@d/@baseSize/2)*1rem; } .width(@px) { width: (@px/@baseSize/2)*1rem; } .height(@px) { height: (@px/@baseSize/2)*1rem; } .min-width(@px) { min-width: (@px/@baseSize/2)*1rem; } .max-width(@px) { max-width: (@px/@baseSize/2)*1rem; } .min-height(@px) { min-height: (@px/@baseSize/2)*1rem; } .max-height(@px) { max-height: (@px/@baseSize/2)*1rem; } .line-height(@px) { line-height: (@px/@baseSize/2)*1rem; } .margin-right(@px) { margin-right: (@px/@baseSize/2)*1rem; } .padding-right(@px) { padding-right: (@px/@baseSize/2)*1rem; } .margin-left(@px) { margin-left: (@px/@baseSize/2)*1rem; } .padding-left(@px) { padding-left: (@px/@baseSize/2)*1rem; } .margin-top(@px) { margin-top: (@px/@baseSize/2)*1rem; } .padding-top(@px) { padding-top: (@px/@baseSize/2)*1rem; } .margin-bottom(@px) { margin-bottom: (@px/@baseSize/2)*1rem; } .padding-bottom(@px) { padding-bottom: (@px/@baseSize/2)*1rem; } .border(@px,@color) { border: (@px/@baseSize/2)*1rem solid @color; } .border-top(@px,@color) { border-top: (@px/@baseSize/2)*1rem solid @color; } .border-bottom(@px,@color) { border-bottom: (@px/@baseSize/2)*1rem solid @color; } .border-right(@px,@color) { border-right: (@px/@baseSize/2)*1rem solid @color; } .border-left(@px,@color) { border-left: (@px/@baseSize/2)*1rem solid @color; } // border 不可这样用,暂时废弃~ /*.border(@px,@color) { position: relative; &::after { content: ""; position: absolute; left: 0; top: 0; width: 200%; height: 200%; border: @px solid @color; color: @color; -webkit-transform-origin: left top; transform-origin: left top; -webkit-transform: scale(0.5,0.5); transform: scale(0.5,0.5); pointer-events: none; !* 防止点击触发 *! box-sizing: border-box; } }*/ .border-radius(@px) { border-radius: (@px/@baseSize/2)*1rem; } .border-radius(@a,@b,@c,@d) { border-radius: (@a/@baseSize/2)*1rem (@b/@baseSize/2)*1rem (@c/@baseSize/2)*1rem (@d/@baseSize/2)*1rem; } .border-bottom-right-radius(@px) { border-bottom-right-radius: (@px/@baseSize/2)*1rem; } /*右上角圆角*/ .border-top-right-radius(@px) { border-top-right-radius: (@px/@baseSize/2)*1rem; } /*左下角圆角*/ .border-bottom-left-radius(@px) { border-bottom-left-radius: (@px/@baseSize/2)*1rem; } /*左上角圆角*/ .border-top-left-radius(@px) { border-top-left-radius: (@px/@baseSize/2)*1rem; } .border-width(@a,@b,@c,@d) { border-width: (@a/@baseSize/2)*1rem (@b/@baseSize/2)*1rem (@c/@baseSize/2)*1rem (@d/@baseSize/2)*1rem; } .top(@px) { top: (@px/@baseSize/2)*1rem; } .left(@px) { left: (@px/@baseSize/2)*1rem; } .right(@px) { right: (@px/@baseSize/2)*1rem; } .bottom(@px) { bottom: (@px/@baseSize/2)*1rem; } .flex-basis(@px) { flex-basis: (@px/@baseSize/2)*1rem; } // 根元素大小使用 vw 单位 html { font-size: (@baseSize/(@baseDesign / 2)) * 100vw; @media screen and (orientation: landscape) { font-size: (@baseSize/(@baseDesign / 2)) * 100vh; } // 同时,通过Media Queries 限制根元素最大最小值 @media screen and (orientation: portrait) and (max-width: 320px) { font-size: 64px; } @media screen and (orientation: portrait) and (min-width: 540px) { font-size: 108px; } /*@media screen and (orientation: landscape) and (min-width: 1023px) and (max-width:1024px) { font-size: (@baseSize/(@baseDesign / 2)) * 100vw; }*/ } @imgPath: "../../assets/images/"; |
---|
目前前端模版大部分采用了此方案,由于页面使用less写的样式,不是scss,所以不能直接通过函数传入参数转为对应的rem值,最终的解决办法是把常用的可能会用到的尺寸属性全部用rem的形式表示出来,写一个mixin.less,其他页面直接使用对应的方法,传入设计图尺寸,单位默认为px。
此方法实现的适配,可以很好的做平板的适配,只需要限制最大font-size就可以,对于横屏下显示的字体大小设置,个人感觉一个很方便的方法,直接在横屏下所有的vw改为vh即可,唯独在页面是横屏但宽高差别不大的时候,需要用媒体查询做个适配,用vw更合理。
- TechEmpower 13轮测试中的ASP.NET Core性能测试
- 反馈型神经网络
- (Head First 设计模式)学习笔记(1)
- [c#]Webservice中如何实现方法重载(overload)以及如何传送不能序列化的对象作参数
- Web.Config文件配置小记
- [原创]web application中使用Profile应该注意的问题
- MRTG FOR WINDOWS 安装指南
- 几种常见复合sql查询语句的linq写法[继续补充中]
- [原创]在msmq3.0中使用http协议发送消息
- 微信小程序开发探索之路
- 前端周记 2017 年终总结
- asp.net mvc中的路径选择
- MVC中实现加载更多
- 在ASP.NET MVC 中获取当前URL、controller、action
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- laravel5.6 框架操作数据 Eloquent ORM用法示例
- Python版名片管理系统
- docker-compose部署php项目实例详解
- Linux下通过sed命令对kv方式的配置文件进行修改
- laravel5.6框架操作数据curd写法(查询构建器)实例分析
- PHP基于timestamp和nonce实现的防止重放攻击方案分析
- 怎么在 Linux 中查找一个命令或进程的执行时间
- laravel5.6 框架邮件队列database驱动简单demo示例
- php layui实现前端多图上传实例
- 解决Centos7下crontab+shell脚本定期自动删除文件问题
- PHP使用ajax的post方式下载excel文件简单示例
- laravel邮件发送的实现代码示例
- php curl发送请求实例方法
- ubuntn备份办法总结(四种)
- PHP中散列密码的安全性分析