移动端适配(必须要知道的,亲测有效)

news/2024/11/24 9:58:23/

关于移动端适配(必须要知道的,亲测有效)

  • 一、各种单位概念理解
  • 二、移动,web开发
  • 三、移动端适配
    • 1、视口(viewport)概念
    • 2、视口(viewport)适配(代码)
    • 3、rem单位适配
      • flexible方案
      • 竖屏、横屏、ipad、PC最全的适配JS
    • 4、长屏短屏布局适配
    • 5、横竖屏适配
      • css媒体查询(CSS Media Queries)
      • window.orientation旋转事件
    • window.innerHeight/window.innerWidth
      • 软键盘弹出-影响横竖屏判断的坑
      • 强制横竖屏时遇到的坑
      • 横竖屏切换,全屏背景的坑
      • 在游戏接入中-H5隐藏某些模块
    • 6、iphone适配
      • 概念
      • h5、小程序适配代码
    • 7、常见适配问题
      • 1px问题
      • 图片模糊问题

移动端适配,在开发中经常会遇到的问题:

一、各种单位概念理解

英寸:屏幕的物理大小,如电脑显示器的17、22,手机显示器的4.8、5.7等使用的单位都是英寸。一般说的屏幕的尺寸都是屏幕对角线的长度。1英寸 = 2.54 厘米

像素:像素即一个小方块,它具有特定的位置和颜色。图片、电子屏幕(手机、电脑)就是由无数个具有特定颜色和特定位置的小方块拼接而成。像素可以作为图片或电子屏幕的最小组成单位。

分辨率:屏幕分辨率(一个屏幕具体由多少个像素点组成。),图像分辨率(指图片含有的像素数。)。同一尺寸的屏幕、图片,分辨率越高,越清晰。
PPI(Pixel Per Inch):每英寸包括的像素数。PPI越高,越清晰。
DPI(Dot Per Inch):即每英寸包括的点数。DPI和PPI是等价的。

DIP或DP 设备独立像素:用来告诉不同分辨率的手机,它们在界面上显示元素的大小是多少

dpr(device pixel ratio)设备像素比:物理像素和设备独立像素的比值

//web
let dpr = window.devicePixelRatio;//css 使用媒体查询min-device-pixel-ratio区分
@media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2){ }//React Native
PixelRatio.get()

web前端长度单位详解(px、em、rem、%、vw/vh、vmin/vmax、vm、calc())

二、移动,web开发

移动端开发
1、在iOS、Android和React Native开发中样式单位其实都使用的是设备独立像素。

2、移动端开发中,UI给我们的原型图一般是基于iphone6(750*1334)的像素给定的。

3、为了适配所有机型,我们在写样式时需要把物理像素转换为设备独立像素。

web开发
web端用到最多的单位是px,即CSS像素。当页面缩放比例为100%时,一个CSS像素等于一个设备独立像素
页面的缩放系数 = 理想视口宽度 / 视觉视口宽度

三、移动端适配

1、视口(viewport)概念

视口共包括三种:布局视口视觉视口理想视口,它们在屏幕适配中起着非常重要的作用。

布局视口:在PC浏览器上,布局视口就等于当前浏览器的窗口大小(不包括borders 、margins、滚动条)。在移动端,布局视口被赋予一个默认值,大部分为980px

在这里插入图片描述

// 获取布局视口大小
document.documentElement.clientWidth / clientHeight

视觉视口:用户通过屏幕真实看到的区域。默认等于当前浏览器的窗口大小(包括滚动条宽度)

在这里插入图片描述

// 获取视觉视口大小
window.innerWidth / innerHeight

理想视口:网站页面在移动端展示的理想大小。在浏览器调试移动端时页面上给定的像素大小就是理想视口大小,它的单位正是设备独立像素。

screen.width / height // 获取理想视口大小

获取浏览器各种视口大小:
在这里插入图片描述

2、视口(viewport)适配(代码)

<meta> 元素 元数据信息。 告诉浏览器如何解析页面。可以借助<meta>元素的viewport来帮助我们设置视口、缩放等,从而让移动端得到更好的展示效果
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />

在这里插入图片描述
为了在移动端让页面获得更好的显示效果,我们必须让布局视口、视觉视口都尽可能等于理想视口。
设置width=device-width就相当于让布局视口等于理想视口
设置initial-scale=1;就相当于让视觉视口等于理想视口
这时,1个CSS像素就等于1个设备独立像素,而且我们也是基于理想视口来进行布局的,所以呈现出来的页面布局在各种设备上都能大致相似。

3、rem单位适配

flexible方案

核心代码:

	// set 1rem = viewWidth / 100function setRemUnit() {var rem = docEl.clientWidth / 7.5docEl.style.fontSize = rem + 'px'}setRemUnit()

竖屏、横屏、ipad、PC最全的适配JS

<script type="text/javascript">// 设备区分 (安卓、火狐、平板、PC)var os = function() {var ua = navigator.userAgent,isAndroid = /(?:Android)/.test(ua),isFireFox = /(?:Firefox)/.test(ua),isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)),isPC = !(/Android|webOS|iPhone|iPod|BlackBerry/i.test(ua));return {isTablet: isTablet,};}();(function (window, document) {function resize() {var docEl = document.documentElement;var clientWidth = docEl.clientWidth;var clientHeight = docEl.clientHeight;if (clientWidth < clientHeight) { // 手机竖屏docEl.style.fontSize = clientWidth / 7.5 + "px";} else { // 手机横屏 docEl.style.fontSize = clientHeight / 7.5 + "px";}if(os.isTablet) { //ipad if(clientWidth >= 1366) {docEl.style.fontSize = clientWidth / 1366 * 100 + "px";} else {docEl.style.fontSize = clientWidth / 1024 * 100 + "px";}}if (os.isPC) { // pcif(clientWidth >= 1920) {docEl.style.fontSize = 1920 / 19.20 + "px";} else {docEl.style.fontSize = clientWidth / 19.20 + "px";}} }resize();// reset rem unit on page resizewindow.addEventListener("resize", function () {resize()});window.addEventListener('pageshow', function (e) {if (e.persisted) {resize()}});}(window, document));
</script>

4、长屏短屏布局适配

rem(font size of the root element)是指相对于根元素的字体大小的单位。虽然不同手机的宽高尺寸不一样,但是设置了root font-size之后,每个手机的宽度都是等量的rem,可以理解为每个手机的宽度都是一样的,区别在于高度不一样。
在这里插入图片描述
有些单屏页面内容很少,在短屏上超出一屏、刚好,在长屏上只占页面顶上一小部分,布局会显得不协调。

长屏短屏布局适配方案:
1、css媒体查询,适当扩展间距让内容撑满更多高度
2、垂直定位尽量用%,不要用固定单位

5、横竖屏适配

css媒体查询(CSS Media Queries)

@media screen and (orientation: portrait) {/* 竖屏 */
}
@media screen and (orientation: landscape) {/*横屏 css*/
}

window.orientation旋转事件

通过绑定orientationchange旋转事件来判断横竖屏。
在 iOS 平台以及大部分 Android 手机都有支持这个属性,它返回一个与默认屏幕方向偏离的角度值:
0:代表此时是默认屏幕方向
90:代表顺时针偏离默认屏幕方向90度
-90:代表逆时针偏离默认屏幕方向90度
180:代表偏离默认屏幕方向180度
如下图所示:

在实际应用中,对于 iPhone 和大部分 Android 是没有180度的手机竖屏翻转的情况的,但是 iPad 是存在的。

function recordOrient() {if (window.orientation === 180 || window.orientation === 0) {//竖屏} else if (window.orientation === 90 || window.orientation === -90) {// 横屏}
}recordOrient();
window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function () {recordOrient();
}, false);

弹框型js完美方案:

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title></title>
<script type="text/javascript">(function (window, document) {function resize() {var docEl = document.documentElement;let clientWidth = docEl.clientWidth;let clientHeight = docEl.clientHeight;var k = 375;if (window.orientation === 180 || window.orientation === 0) {//竖屏} else if (window.orientation === 90 || window.orientation === -90) {//横屏// k = 320;}if (clientWidth <= k) { //竖屏docEl.style.fontSize = clientWidth / 750 * 100 + "px";} else { //横屏docEl.style.fontSize = k / 750 * 100 + "px";}}resize();// reset rem unit on page resizewindow.addEventListener("resize", function () {resize()});window.addEventListener('pageshow', function (e) {if (e.persisted) {resize()}});}(window, document));</script>

页面型完美解决方案:

(function (win, doc) {if (!win.addEventListener) return;var html = document.documentElement;function setFont() {var html = document.documentElement;var k = 750;html.style.fontSize = html.clientWidth / k * 100 + "px";}setFont();setTimeout(function () {setFont();}, 300);doc.addEventListener('DOMContentLoaded', setFont, false);win.addEventListener('resize', setFont, false);win.addEventListener('load', setFont, false);})(window, document);

window.innerHeight/window.innerWidth

通过比较页面的宽高,当页面的高大于等于宽时则认为是竖屏,反之则为横屏。

function detectOrient(){if(window.innerHeight >= window.innerWidth) {// 竖屏}else {// 横屏 }
}
detectOrient();
window.addEventListener('resize',detectOrient);

软键盘弹出-影响横竖屏判断的坑

在 Android 下,如果页面中出现软键盘弹出的情况(存在有 Input 的元素)时,页面有时会因为软键盘的弹出而导致页面回缩,即页面的宽度(竖屏时)或者高度(横屏时)被改变。
所以通过 CSS多媒体查询(CSS Media Queries) 、 window.matchMedia() 方法、window.innerWidth /window.innerHeight的页面宽高比对方法来实现的横竖屏判断方法,都会因此受到影响,出现判断失误的情况(vivo x9、华为p9、Samsung SCH-i699 机型,在竖屏时由于软键盘弹出导致页面高度小于宽度,被错误地判定为横屏)。
在这样的情况下,这几种方式也变得不可靠。

解决方案:类似的,在横屏判断中,给横屏一个最小判断宽度,低于那个宽度就还是判断为竖屏

CSS 媒体查询:给一个横屏的最小宽度,即使高度很小,通过宽度还是可以区分横竖屏
@media screen and (orientation: portrait) {/* 竖屏 */
}
@media screen and (orientation: landscape) and (min-width: 560px) {/* 横屏 */
}

vue 软件盘弹出 css解决方案:

<template><div ref="app" id="app"><keep-alive><router-view v-if="$route.meta.keepAlive"></router-view></keep-alive><router-view v-if="!$route.meta.keepAlive"></router-view><shine-landscape></shine-landscape> // 组件</div>
</template>
<style lang="less" scoped>
// 横屏 && 软键盘弹出横屏
@media screen and (orientation: landscape) and (min-width: 560px) {/deep/ .shine-landscape{display: block;}
}/style>

js 解决方案:

 let sw = window.screen.width;let sh = window.screen.height;let cw = document.documentElement.clientWidth;if(cw == sw) {// 竖屏this.showShineLandscape = false}if(cw == sh) {// 横屏this.showShineLandscape = true}

强制横竖屏时遇到的坑

【问题】在 iOS 锁屏情况下,强制竖屏不成功
【方案】做一个横屏适配页,内容可以是提示开启锁屏并自动旋转手机屏幕
【注意】做适配时,如果发现在 iOS平台,横屏时页面左右会有默认安全区域空白,我们需要定义页面宽高等于手机视口宽高

@media screen and (orientation: landscape) and (min-width: 560px) {.page{/* 定义页面宽高等于手机视口宽高 */width: 100vw;height: 100vh;background: #fff;position: relative;}
}

【问题】做了强制横竖屏,绝对不能使用window.orientation判断来做横竖屏适配,因为 ios平台 旋转屏幕会改变横竖屏的值从而切换样式,但视图布局为始终保持强制的状态,这就会导致旋转屏幕时页面样式混乱。
【方案】:还是建议CSS多媒体查询,比较简单,解决缺陷的方式也简单

【问题】横竖屏旋转canvas需要重新绘制
【问题】页面中凡是 canvas绘制而成的局部视图,横竖屏旋转,样式不会跟着调整,
【方案】监听旋转屏幕事件,旋转后刷新重绘canvas部分

<template>
<div><template v-if="!isOrientationChange"><canvas id="my-canvas"/></template>
</div>
</template>data() {return {isOrientationChange: false}
},
created() {const _this = this;window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function () {_this.isOrientationChange = true;setTimeout(()=> {_this.isOrientationChange = false;}, 100)}, false);
}

横竖屏切换,全屏背景的坑

【问题】ios安全区域页面留白,背景容器100%的漏洞
【解决方案】 背景容器设置100vw,vh

<div id="parent" class="parent"><div class="parent__banner"><img class="parent__img-portrait" src="./img/banner-portrait.png" alt="竖屏背景"><img class="parent__img-landscape" src="./img/banner-landscape.png" alt="横屏背景"></div>
</div>
.parent{/* vw,vh适配ios安全区域页面留白的漏洞 */width: 100vw;height: 100vh;background-color: #010c0f;position: relative;display: flex;align-items: center;justify-content: center;
}
.parent__banner{width: 100vw;height: 100vh;position: fixed;top: 0;left: 0;
}
.parent__img-portrait{display: block;width: 100%;height: 100%;object-fit:cover;
}@media screen and (orientation: portrait) {/* 竖屏 */.parent__img-landscape{display: none;}
}
@media screen and (orientation: landscape) {/*横屏 css*/.parent__img-landscape{display: none;}
}

在游戏接入中-H5隐藏某些模块

从游戏页面操作跳转到H5页面。采用了window.location的跳转方式,这里不能忘记手动携带该有的参数,否则从这里开始接下来所有页面都失去了判断的参数,项目中该去掉的模块就会出现

/*** 获得时间戳*/
function randomTimeStamp() {return parseInt(new Date().getTime() / 1000) + '';
}
/*** 检测flag* @param search*/
function checkFlag(search) {if (/[&|?](flag=[\d]+)/.test(search)) {search = search.replace(/[&|?](flag=[\d]+)/, (str) => {let splitStrs = str.split('=');return splitStrs[0] + '=' + randomTimeStamp();})} else {if (search) {search += '&flag=' + randomTimeStamp();} else {search += '?flag=' + randomTimeStamp();}} return search;
}
/*** 拼接地址* @param path: 路由* @param flag: 是否需要刷新*/
function locationUrl(path, flag = true) {let url = '';if (flag) {let search = checkFlag(location.search);url = location.protocol + '//' + location.host + location.pathname + search + '#/' + path;} else {url = location.protocol + '//' + location.host + location.pathname + location.search + '#/' + path;}window.location = url;
}this.locationUrl('Index');

6、iphone适配

概念

安全区域适配
iphone x\xr\xs\11 pro 取消了物理按键,改成底部小黑条,这一改动导致网页出现了比较尴尬的屏幕适配问题。
对于网页而言,顶部(刘海部位)的适配问题浏览器已经做了处理,所以我们只需要关注底部与小黑条的适配问题即可(即常见的吸底导航、返回顶部等各种相对底部 fixed 定位的元素)。如下图:
在这里插入图片描述
安全区域:一个可视窗口范围,处于安全区域的内容不受圆角(corners)、齐刘海(sensor housing)、小黑条(Home Indicator)影响。

viewport-fit:iOS11 新增特性,苹果公司为了适配 iPhoneX 对现有 viewport meta 标签的一个扩展,用于设置网页在可视窗口的布局方式,可设置 三个值:
auto \ contain: 可视窗口完全包含网页内容(左图)。页面内容显示在safe area内
cover:网页内容完全覆盖可视窗口(右图)。页面内容充满屏幕。
在这里插入图片描述
env() 和 constant():iOS11 新增特性,Webkit 的一个 CSS 函数,用于设定安全区域与边界的距离,有四个预定义的变量:

safe-area-inset-left:安全区域距离左边边界距离
safe-area-inset-right:安全区域距离右边边界距离
safe-area-inset-top:安全区域距离顶部边界距离
safe-area-inset-bottom:安全区域距离底部边界距离

【注意】:
1)网页默认不添加扩展的表现是 viewport-fit=contain,需要适配 iPhoneX 必须设置 viewport-fit=cover,不然 constant 函数是不起作用的,这是适配的必要条件
2)env() 是官方文档中提到将来要替换 constant (),目前还不可用。
为此目前我们可以看作 constant:针对iOS < 11.2以下系统,env:针对于iOS >= 11.2的系统
使用的时候 env() 必须写在 constant () 后面

h5、小程序适配代码

第一步:设置网页在可视窗口的布局方式 (viewport-fit=cover")

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />

第二步:页面主体内容限定在安全区域内

body {padding-top: constant(safe-area-inset-top);  padding-top: env(safe-area-inset-top);  padding-bottom: constant(safe-area-inset-bottom);padding-bottom: env(safe-area-inset-bottom);
}

第三步:相对底部 fixed 定位的元素适配
1、fixed吸底 元素(bottom = 0)

<div class="footer ipx-padding-safe-area"></div>
// 或者
<div class="footer ipx-height-safe-area"></div>
// 或者
<div class="footer ipx-margin-safe-area"></div>
<div class="ipx-footer-safe-area"></div>
/* 通过加内边距 padding 扩展高度 */
.ipx-padding-safe-area{padding-bottom: constant(safe-area-inset-bottom);padding-bottom: env(safe-area-inset-bottom);
}
/* 通过计算函数 calc 覆盖原来高度 */
.ipx-height-safe-area{height: calc(1rem +  constant(safe-area-inset-bottom));height: calc(1rem +  env(safe-area-inset-bottom));
}
/* 通过加外边距 margin 增加底部距离 */
.ipx-margin-safe-area{margin-bottom: constant(safe-area-inset-bottom);margin-bottom: env(safe-area-inset-bottom);
}
/* 
以上方式需要吸底条必须是有背景色的,因为扩展的部分背景是跟随外容器的,否则出现镂空情况。 
还有一种方案就是,可以通过新增一个新的元素(空的颜色块,主要用于小黑条高度的占位),然后吸底元素可以不改变高度只需要调整位置
*/
.ipx-footer-safe-area{position: fixed;bottom: 0;width: 100%;height: constant(safe-area-inset-bottom);height: env(safe-area-inset-bottom);background-color: #fff;
}

2、fixed 非完全吸底元素(bottom ≠ 0),比如 “返回顶部”、“侧边广告” 等

<div class="back-top ipx-margin-safe-area">返回顶部</div>

第四步:使用 @supports 隔离兼容样式(可有可无)

@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {/* 通过加内边距 padding 扩展高度 */.ipx-padding-safe-area{padding-bottom: constant(safe-area-inset-bottom);padding-bottom: env(safe-area-inset-bottom);}...
}

7、常见适配问题

1px问题

【问题】:在设备像素比大于1的屏幕上,我们写的1px实际上是被多个物理像素渲染,这就会出现1px在有些屏幕上看起来很粗的现象
【方案】:伪类 + transform 基于media查询判断不同的设备像素比对线条进行缩放

/* 边框线条 top\bottom */
.border-top, .border-bottom{position: relative;
}
.border-top:before{content: '';position: absolute;top: 0;height: 1px;width: 100%;background-color: #000;transform-origin: 50% 0%;
}
.border-bottom:before{content: '';position: absolute;bottom: 0;height: 1px;width: 100%;background-color: #000;transform-origin: 50% 0%;
}
@media only screen and (-webkit-min-device-pixel-ratio:2){.border-top:before, .border-bottom:before{transform: scaleY(0.5);}
}
@media only screen and (-webkit-min-device-pixel-ratio:3){.border_1px:before, .border-bottom:before{transform: scaleY(0.33);}
}

图片模糊问题

【问题】:我们平时使用的图片大多数都属于位图(png、jpg…)。在dpr > 1的屏幕上,位图的一个像素可能由多个物理像素来渲染,然而这些物理像素点并不能被准确的分配上对应位图像素的颜色,只能取近似值,所以相同的图片在dpr > 1的屏幕上就会模糊
【方案】:在dpr=2的屏幕上展示两倍图(@2x),在dpr=3的屏幕上展示三倍图(@3x)

1、媒体查询缩放(只适用于背景图)

@media only screen and (-webkit-min-device-pixel-ratio:2){.bg1{transform: scaleY(0.5);}
}
@media only screen and (-webkit-min-device-pixel-ratio:3){.bg1{transform: scaleY(0.33);}
}

2、image-set

.avatar {background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x );
}

3、使用img标签的srcset属性

<img src="conardLi_1x.png"srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">

4、使用svg可缩放矢量图


http://www.ppmy.cn/news/115577.html

相关文章

hdu6199

沈阳网络赛1006 gems gems gems 题意是有n堆宝石(可能有负数),A和B从左到右拿宝石&#xff0c;A先手拿1或者2堆&#xff0c;假设某个人当前拿了k堆&#xff0c;那么下一个人只能拿k或者k1堆&#xff0c;如果他取不了k堆宝石时&#xff0c;游戏结束。定义difference为A拿到的宝…

掌握这个90%的人都不会的大屏技术,裁员、降薪与你无关

裁员话题时不时就被拉到热搜上溜几圈&#xff0c;一方面让各位打工人们焦虑恐惧失业风险&#xff0c;另一方面也能让各位从一波波裁员危机事件中吸取“经验”。例如&#xff0c;技术人员狂敲代码、业务人员猛冲业绩…该被裁的依旧如此&#xff0c;在当今你得具备点别人没有的技…

2305d很不错的模拟技术

D很不错. 这里有个. 今天,我正在编写一些迭代数据结构并把输出写进一堆不同文件的代码.它像这样: void genSplitHtml(Data data, ...) {auto outputTemplate File("template.html", "r");foreach (...) {auto filename generateFilename(...);auto sin…

Linux之Shell编程

变量 nano hello.sh 编辑hello.sh这个shell脚本文件&#xff0c;如写上echo hello!!!。其中&#xff0c;在编辑的最开始加上#!/bin/bash 表示该脚本文件用bash执行&#xff0c;#是注释&#xff0c;#!不是注释。 bash/source/. hello.sh 执行hello.sh&#xff0c;如输出hello!…

1599

#include<iostream> using namespace std; int main() {int n,x,a;cin>>n>>a>>x;int s n - a*x;cout<<s;return 0; }

系统学习】Java基础3之反射

Java反射 反射机制概述 Reflection&#xff08;反射&#xff09;是被视为动态语言的关键&#xff0c;反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息&#xff0c;并能直接操作任意对象的内 部属性及方法 加载完类之后&#xff0c;在堆内存的方法区中就产…

DS90UB949

DS90UB949 EVM Q1 1) Generate EDID file Deltacast E-EDID Editor Save as : xxx.bin 2) Flash xxx.bin to EEPROM. 3) Plugin EEPROM into board 4) remove R11: HPD R13:DDC-CLK R14: DDC-SDA

98 服务器系统,Windows 98

Windows 98 当时依旧有了添加/删除面板&#xff0c;但是只能够删除桌面的图标&#xff0c;不像后来的Windows XP系统&#xff0c;后者已经可以通过控制面板进行卸载应用程序等更复杂的操作。 Windows 98 Windows 98全面集成了Internet标准&#xff0c;以Internet技术统一并简化…