移动端适配,在开发中会遇到很多问题
- 1px问题
- UI图完美适配方案
- 横屏适配
- 高清屏图片模糊问题 ...
适配的意义
元素、字体大小能随着屏幕大小(一般相对于宽度来说)变化而变化,尺寸的大小和屏幕大小成正比。
如何适配
首先应设置viewport视口
meta标签
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
解析:
解决适配的方法
第一种:媒体查询(设定每种屏幕对应的font-size)
@media screen and (min-width:240px) {
html, body, button, input, select, textarea {
font-size:9px;
}
}
@media screen and (min-width:320px) {
html, body, button, input, select, textarea {
font-size:12px;
}
}
@media screen and (min-width:1000px) and (max-width: 1200px){
html, body, button, input, select, textarea{
font-size:16px
}
}
⚠️:使用min-width时,小的在前面,大的在后面;同理,如果使用max-width时,就是大的在前面,小的在后面。
第二种:rem
布局思想: -一般不要给元素设置具体的宽度,但是对于一些小图标可以设定具体宽度指
- 高度值可以设置固定值,设计稿多大,就设置多大。
- 所有设置的固定值都用rem做单位(首先在HTML总设置一个基准值:px和rem的对应比例,然后在效果图上获取px值,布局的时候转化为rem值。)
- js获取真实屏幕的宽度,让其除以设计稿的宽度,算出比例,把之前的基准值按照比例进行重新的设定。
rem是一个相对单位,1rem等于html元素上字体设置的大小。只需要设置html根元素的font-size的大小,就可以改变rem所代表的大小。
(1) 动态设置html的font-size
PC端
(function () {
function setRootFontSize() {
let rem, rootWidth;
let rootHtml = document.documentElement;
//限制展现页面的最小宽度
rootWidth = rootHtml.clientWidth < 1366 ? 1366 : rootHtml.clientWidth;
// 19.2 = 设计图尺寸宽 / 100( 设计图的rem = 100 )
rem = rootWidth / 19.2;
// 动态写入样式
rootHtml.style.fontSize = `${rem}px`;
}
setRootFontSize();
window.addEventListener("resize", setRootFontSize, false);
})();
移动端
(function () {
function setRootFontSize() {
let dpr, rem, scale, rootWidth;
let rootHtml = document.documentElement;
dpr = window.devicePixelRatio || 1; //移动端必须设置
//限制展现页面的最小宽度
rootWidth = rootHtml.clientWidth < 1366 ? 1366 : rootHtml.clientWidth;
rem = rootWidth * dpr / 19.2; // 19.2 = 设计图尺寸宽1920 / 100(设计图的rem = 100)
scale = 1 / dpr;
// 设置viewport,进行缩放,达到高清效果 (移动端添加)
let vp = document.querySelector('meta[name="viewport"]');
vp.setAttribute('content', 'width=' + dpr * rootHtml.clientWidth + ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no');
// 动态写入样式
rootHtml.style.fontSize = `${rem}px`;
}
setRootFontSize();
window.addEventListener("resize", setRootFontSize, false);
window.addEventListener("orientationchange", setRootFontSize, false); //移动端
})();
另一种写法: 在webpack中使用px2rem-loader
rules:[
{test:/\.css$/,
use:['style-loader','css-loader','px2rem-loader?remUnit=75&remPrecision=8']
}
]
// 解决常见的750px的设计图,直接按照设计图的尺寸来填写属性大小就好
// 注意点:html文件需要引入flexible文件
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.2/??flexible_css.js,flexible.js"></script>
(2) 前端构建中,可以利用scss来解决这个问题
那么如何把UE设计稿中的获取的像素单位的值,转换为已rem为单位的值呢?公式是:元素宽度 / UE图宽度 * 100。
让我们举个例子,假设UE图尺寸是640px,UE图中的一个元素宽度是100px,根据公式100/640*100 = 15.625
p {width: 15.625rem}
逐一的计算太麻烦,我们可以写一个scss的function px2rem即:
$ue-width: 640; /* ue图的宽度 */
@function px2rem($px) {
@return #{$px/$ue-width*100}rem;
}
p {
width: px2rem(100);
}
第三种:vw (视口宽度的 1/100), vh (视口高度的 1/100)
vh、vw方案即将视觉视口宽度 window.innerWidth和视觉视口高度 window.innerHeight 等分为 100 份
webpack解析css 的时候用postcss-loader 有个postcss-px-to-viewport能自动实现px到vw的转化.
{
loader: 'postcss-loader',
options: {
plugins: ()=>[
require('autoprefixer')({
browsers: ['last 5 versions']
}),
require('postcss-px-to-viewport')({
viewportWidth: 375, //视口宽度(数字)
viewportHeight: 1334, //视口高度(数字)
unitPrecision: 3, //设置的保留小数位数(数字)
viewportUnit: 'vw', //设置要转换的单位(字符串)
selectorBlackList: ['.ignore', '.hairlines'], //不需要进行转换的类名(数组)
minPixelValue: 1, //设置要替换的最小像素值(数字)
mediaQuery: false //允许在媒体查询中转换px(true/false)
})
]
}
第四种:flex布局(自适应)
原则:文字流式,控件弹性,图片等比缩放。
第五种:postcss-px2rem插件
- 第一步:在pack.json文件的webpack中配置postcss-px2rem插件
css:{
loaderOptions:{
postcss:{
plugins:[
reuqire('postcss-px2rem')({remUnit:100}) //换算的基准
]
}
}
}
- 第二步:正常的css写法。注意:border为1px时,部分机型可能看不到,加上no,禁止rem转换。
title:{
margin:70px;
width:200px;
border: 1px solid red; /*no*/
}
自适应方案的对比
- 媒体查询 如果在浏览器大小改变时,需要改变的样式太多,那么多套样式代码会很繁琐。
- rem布局 在响应式布局中,必须通过js来动态控制根元素font-size的大小。
移动端1像素显示变粗问题
px是一个相对单位,相对的是设备像素。
- transform: scale(0.5) 方案
div {
height:1px;
background:#000;
-webkit-transform: scaleY(0.5);
-webkit-transform-origin:0 0;
overflow: hidden;
}
- css根据设备像素比媒体查询后的解决方案
/* 2倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 2.0) {
.border-bottom::after {
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
}
/* 3倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 3.0) {
.border-bottom::after {
-webkit-transform: scaleY(0.33);
transform: scaleY(0.33);
}
}
图片模糊问题
原因
平时使用的图片大多数都属于位图(png,jpg...),位图由一个个像素点构成的,每个像素都具有特定的位置和颜色值。
解决方案
为了保证图片质量,应该尽可能让一个屏幕像素来渲染一个图片像素,针对不同的DPR的屏幕,需要展示不同分辨率的图片。
- 使用media查询判断不同的设备像素比来显示不同精度的图片(只适用于背景图)
.avatar {
background-image:url(img_1.png)
}
@media only screen and (-webkit-device-pixel-ratio:2){
.avatar {
background-image:url(img_2.png)
}
}
@media only screen and (-webkit-device-pixel-ratio:3){
.avatar {
background-image:url(img_3.png)
}
}
- image-set(只适用于背景图)
.avatar {
background-image:-webkit-image-set("img_1.png" 1x,"img_2.png" 2x,"img_3.png" 3x)
}
- srcset 使用img标签的属性,浏览器会自动根据像素密度匹配最佳显示图片
<img src="img_1.png" srcset="img_2.png 2x,img_3.png 3x">
- JavaScript拼接图片URL 使用window.devicePixelRatio获取设备像素比,遍历所有图片,替换图片地址
const dpr = window.devicePixelRatio
const images = document.querySelectorAll('img');
images.forEach((img)=>{
img.src.replace('.','@${dpr}x.')
})
横屏适配
- window.orientation:获取屏幕旋转方向
window.addEventListener('resize',()=>{
if(window.orientation === 180 || window.orientation === 0) {
// 正常方向或屏幕旋转180度
console.log('竖屏')
};
if(window.orientation === 90 || window.orientation === -90) {
// 屏幕顺时针旋转90度或屏幕逆时针旋转90度
console.log('横屏')
}
})
- CSS检测横屏
@media screen and (orientation:portrait){
/*竖屏*/
}
@media screen and (orientation:landscape){
/*横屏*/
}