移动端适配就是在进行屏幕宽度的等比例缩放

文中我强调了移动端适配是对屏幕宽度进行缩放:对于普通的流式布局(长屏幕页面),页面内容是可以上下滚动的。屏幕小,一屏幕看到的东西虽然变少,但是用户可以通过手势滚动页面,继续浏览下一屏的内容。因此在常规情况下,对于屏幕宽度进行等比例缩放已经能解决大部分应用场景了

但是对于一种特殊的场景:单屏页面(又称翻屏页面),由于需要把一整屏的内容完整展示给用户,同时又要求页面不能出现滚动条,那么,仅仅只是针对屏幕宽度进行等比例缩放的适配,其实效果并不理想

设备独立像素 (CSS像素)

设备独立像素是一种可以被程序所控制的虚拟像素,在Web开发中对应CSS像素

iphone7举例:

iphone7设备独立像素375 * 667

也就是手机全屏下的大小,同时也是chrome模拟器展示的尺寸

可以通过js的screen.widthscreen.height获取

设备像素 (物理像素)

设备像素也可以叫物理像素,由设备的屏幕决定,其实就是屏幕中控制显示的最小单位

iphone7举例:

iphone7设备像素750 * 1334

750 * 1334这个尺寸也可以称为设计像素,我们设计和开发页面时,就是以这个设计像素为准

设备像素比(DPR)

设备像素比(dpr) = 设备像素 / 设备独立像素

iphone7举例:

iphone7dpr = 2 = 750 / 375

也就是说,在iphone7下,1 css像素 = 2 物理像素

css中一个1x1大小的正方形里面,其实有4个物理像素

dpr大于2的屏幕也称为视网膜屏幕(Retina)

实际物理像素

iphone7的实际物理像素是750 * 1334,刚好等于设备像素。但不是所有的设备都是实际物理像素等于设备像素

iphone7 plus的实际物理像素是1080 * 1920。它的dpr为3,设备独立像素414 * 736,根据公式可以得出,它的设备像素等于1242 x 2208,远大于实际物理像素。手机会自动把1242 * 2208个像素点塞进1080 * 1920个物理像素点来渲染,我们不用关心这个过程

单屏幕

前面介绍了这么多概念,其实在真正开发中,我们主要关心的是设备独立像素设备像素

设备像素 决定了设计稿的尺寸。移动端设计稿一般是750 * 1334 尺寸大小( iPhone6 的设备像素为标准的设计图),因此相对比较固定

设备独立像素 决定了设备的屏幕大小。iOS平台下,屏幕尺寸还算相对固定,但是到了Android平台下,屏幕尺寸那就千奇百怪,百花争鸣了。

特别需要注意的一点:即使设备独立像素确定了大小,我们的网页被用户看到的时候,实际高度还是比设备独立像素的高度小很多

主要原因是:我们的网页往往是在手机的浏览器上访问的,而这些浏览器自带了顶部地址栏和底部工具栏,这两部分的高度又进一步压缩了我们网页展示的高度(如果我们的网页是在第三方客户端内打开的,比如微博,微信,Twitter, Facebook,那么一般只有顶部地址栏)

举个例子,iphone11的设备独立像素是414 * 896

左图是在safari浏览器下:可以看到上下红框部分是浏览器自带的区域,只有蓝框是实际网页展示的高度,这个蓝框的大小是 414 * 715(documentElement.clientWidth/documentElement.clientHeight),已经比设备独立像素的高度少了181像素(896 - 715)

右图是在微信自带浏览器下:可以看到顶部红框部分是浏览器自带的区域,只有蓝框是实际网页展示的高度,这个蓝框的大小是 414 * 804(documentElement.clientWidth/documentElement.clientHeight),也比设备独立像素的高度少了92像素(896 - 804)

收集到的一些常见设备尺寸大小:

品牌 操作系统 设备 设备独立像素 (screen.width/screen.height) 自带浏览器下(clientWidth/clientHeight)
苹果 iOS iPhone 7 375 * 667 375 * 548
iPhone 12 390 * 844 390 * 664
Ipnone 11/XR 414 * 896 414 * 715
iPhone X 375 * 812 375 * 635
华为 安卓 P40 360 * 780 360 * 625
nova 8 SE 360 * 800 360 * 659
Mate 30 424 * 918 424 * 774
荣耀8 360 * 640 360 * 501
P10 360 * 640 360 * 526
畅玩7x 360 * 720 360 * 584
Oppo 安卓 R15x 360 * 780 360 * 650
R17 360 * 780 360 * 628
K1 360 * 780 360 * 622
Xiaomi 安卓 MIX 2 393 * 786 393 * 666
小米10 393 * 851 393 * 720
小米6 360 * 640 360 * 521
k40 393 * 873 393 * 713

单屏难点

设计稿的的宽高比是固定的,但是真实设备的宽高比永远不是统一的,并且网页的可视区域还会随着访问方式(浏览器,APP客户端)有所改变。

同一份设计稿,却要在不同尺寸的设备上,都展示出良好的布局:页面的内容要尽可能完整展示在一屏之中(甚至不能有滚动条)

安全区 + 长背景图

750 * 1334 的设计稿,需要考虑到长屏幕时,页面的展示情况

比如默认750 * 1334大小的内容需要完整展示出来(安全区),长屏幕750 * 1750时,把安全区的内容垂直居中展示即可

此时,我们就需要使用一张长背景图(750 * 1750)上下居中作为整个网页的背景

<style>* {margin: 0;padding: 0;box-sizing: border-box;}html {/* 750px 的设计图,1rem = 100px */font-size: calc(100 * 100vw / 750);}html,body {width: 100%;height: 100%;position: relative;}#app {width: 100%;height: 100%;position: relative;/* 长背景图上下居中 */background: url('./img/bg.jpg') no-repeat center / cover #fff;overflow: hidden;}.safe-content {width: 100%;height: 100%;position: absolute;left: 0; /* 限定安全区的高度 */max-height: 13.34rem;top: 50%;/* 安全区上下居中 */transform: translateY(-50%);}
</style>
<body><div id='app'><div class='safe-content'><div class='block1'></div><div class='block2'></div></div></div>
</body>

之后,我们把页面所有的内容,相对safe-content进行布局

完整页面,点击此处预览 (手机模式查看)

完整代码,直接右键源代码或者点击下方按钮展开

点击展开源代码```

Document

深红

水漾烛光礼盒

蒂普提克香氛蜡烛(70g)*1

krramel沐浴套装*1

请到游戏内【精彩活动-实物周边奖励兑换】

填写领取信息

```

宽高比

上面的方案,对于移动端(屏幕高度大于屏幕宽度)的大部分场景,的确够用了。

但是在折叠屏手机(屏幕宽度和高度差别不大),ipad,pc端(屏幕高度小于屏幕宽度)的设备下,我们的页面就很有可能超出了完整的一屏。

如果此时,父级元素还设置了overflow: hidden;,那么用户甚至不能滑动查看超出屏幕的内容,如果底部是一个可交互的按钮,那么用户就永远不能触发之后的流程了!

问题原因

我们的rem适配方案,是相对于屏幕宽度进行缩放的,但是不同机型的手机,可视区域的宽高比并不固定,因此对于部分手机,页面内容就很有可能出现超出屏幕底部或者底部留有空白

对于底部留有空白,一般发生在可视高度比可视宽度大很多的情况,前面介绍的安全区 + 长背景图方案,就是针对此种情况的解决方案。

而对于超出屏幕底部,一般发生在可视高度和可视宽度相差不大(折叠屏手机),甚至可视高度比可视宽度小(横屏或者pc端)的情况,解决方案一般如下:

  • 使用css进行宽高比判断
  • 使用js进行宽高比判断
  • 使用js动态修改rem大小
  • 使用js动态缩放整体页面
  • 使用vwvh进行布局

aspect-ratio

注意和device-aspect-ratio进行区分,device-aspect-ratio是和设备尺寸进行绑定的,但是我们之前介绍过:网页的可视区域会随着访问方式(浏览器,APP客户端)有所改变,因此aspect-ratio才是我们真正需要的属性。

aspect-ratio 定义输出设备中的页面可见区域宽度与高度的比率

同时它有两个max-aspect-ratiomin-aspect-ratio兄弟属性,可以和max-widthmin-width进行类比:

@media screen and (min-aspect-ratio: 9/16) {// 只要宽高比大于等于9/16,就会执行
}@media screen and (min-aspect-ratio: 3/4) {// 只要宽高比大于等于3/4,就会执行
}@media screen and (min-aspect-ratio: 1/1) {// 只要宽高比大于等于1/1,就会执行
}

对于上面的页面,我们正常的设备独立像素是375 * 667,我们可以这样进行高度划分:

  • 高度大于667:无需调整,我们只怕高度小,不怕高度大,高度大时已经有前面的方案: 安全区 + 长背景图
  • 530-667:还是正常,我们也不需要调整
  • 490-530
  • 375-490
  • 小于375:pc端或者横屏,高度已经比宽度小了
@media screen and (min-aspect-ratio: 375 / 530) {.safe-content {transform: translateY(-50%) scale(0.8);}
}@media screen and (min-aspect-ratio: 375 / 490) {.gift-logo {transform: scale(0.6);}
}@media screen and (min-aspect-ratio: 375 / 375) {.safe-content {transform: translateY(-50%) scale(0.32);}.gift-logo {transform: scale(0.4);}
}

比较简单暴力的,就是直接对主要页面区域施加 transform: scale(0.8) 这类样式,直接缩小。

至于我刚刚划分的高度区间,是通过chrome模拟器自己一次次实验调整出来的,这个要具体页面具体分析,并没有一个统一的宽高比规定。

完整页面,点击此处aspect-ratio-预览 (手机模式查看)

完整代码,直接右键源代码

JS添加全局类

function detectAspectRatio(aspectRatio) {document.documentElement.classList.remove('pc', 'mobile1', 'mobile2');if (aspectRatio >= 375 / 375) {return document.documentElement.classList.add('pc');}if (aspectRatio >= 375 / 490) {return document.documentElement.classList.add('mobile1');}if (aspectRatio >= 375 / 530) {return document.documentElement.classList.add('mobile2');}
}
function init() {const clientWidth = document.documentElement.clientWidth;const clientHeight = document.documentElement.clientHeight;const aspectRatio = clientWidth / clientHeight;detectAspectRatio(aspectRatio);
}
init();
window.addEventListener('resize', init);

本质上就是把css的媒体查询aspect-ratio用js实现了一遍,所以这种方案区别不大。

动态修改rem

默认情况下,我们的rem是根据可视区域宽度进行计算的,但是在高度较小的情况下,我们可以动态的修改rem的参考对象,让它根据可视高度进行计算

我们甚至可以实现:无论窗口怎么变,我们的内容都保持原来的比例,并尽量占满窗口

封装成一个通用flexible.js方法

const defaultConfig = {pageWidth: 750,pageHeight: 1334,pageFontSize: 32,
};const flexible = (config = defaultConfig) => {const {pageWidth = defaultConfig.pageWidth,pageHeight = defaultConfig.pageHeight,pageFontSize = defaultConfig.pageFontSize,} = config;const pageAspectRatio = defaultConfig.pageAspectRatio || (pageWidth / pageHeight);// 根据屏幕大小及dpi调整缩放和大小function onResize() {let clientWidth = document.documentElement.clientWidth;let clientHeight = document.documentElement.clientHeight;let aspectRatio = clientWidth / clientHeight;// 根元素字体let e = 16;if (clientWidth > pageWidth) {// 认为是ipad/pcconsole.log('认为是ipad/pc');e = pageFontSize * (clientHeight / pageHeight);} else if (aspectRatio > pageAspectRatio) {// 宽屏移动端console.log('宽屏移动端');e = pageFontSize * (clientHeight / pageHeight);} else {// 正常移动端console.log('正常移动端');e = pageFontSize * (clientWidth / pageWidth);}document.documentElement.style.fontSize = `${e}px`;let realitySize = parseFloat(window.getComputedStyle(document.documentElement).fontSize);if (e !== realitySize) {e = e * e / realitySize;document.documentElement.style.fontSize = `${e}px`;}}const handleResize = () => {onResize();};window.addEventListener('resize', handleResize);onResize();return (defaultSize) => {window.removeEventListener('resize', handleResize);if (defaultSize) {if (typeof defaultSize === 'string') {document.documentElement.style.fontSize = defaultSize;} else if (typeof defaultSize === 'number') {document.documentElement.style.fontSize = `${defaultSize}px`;}}};
};

使用时:

flexible({ pageFontSize: 100 });
.safe-content {width: 7.5rem;height: 13.34rem;position: absolute;top: 0;bottom: 0;left: 0;right: 0;margin: auto;
}

这时,只需要把safe-contentwitdhheight写死,就能保证宽高比永远保持为750 * 1334

完整页面,点击此处动态修改rem-预览 (手机模式查看)

改变屏幕大小,可以看到safe-content一直都保持正常的宽高比,并且总是宽度占满(宽度比高度小)或者高度占满(高度比宽度小)

完整代码,直接右键源代码

另外一个简化版本

(function init(screenRatioByDesign = 750 / 1334) {let docEle = document.documentElementfunction setHtmlFontSize() {var screenRatio = docEle.clientWidth / docEle.clientHeight;// 7.5 = 750 / 100(100是设计稿上的1rem大小)var fontSize = (screenRatio > screenRatioByDesign? (screenRatioByDesign / screenRatio): 1) * docEle.clientWidth / 7.5;docEle.style.fontSize = fontSize.toFixed(3) + "px";}setHtmlFontSize()window.addEventListener('resize', setHtmlFontSize)
})()

完整页面,点击此处动态修改rem2-预览 (手机模式查看)

动态缩放整体页面

前面的适配方案,我们都借助了rem进行页面的大小缩放。其实我们也可以直接使用px进行页面的布局,最后统一使用js进行整体缩放

 function init() {const winWidth = document.documentElement.clientWidth;const winHeight = document.documentElement.clientHeight;const winScale = winWidth / winHeight;const page = document.querySelector('.safe-content');const pageWidth = 750;const pageHeight = 1334;const pageScale = pageWidth / pageHeight;const origin = '50% 50% 0';let initialScale = 1;if (winScale > pageScale) {// 宽度长了,但是高度不够// 高度占满,宽度水平居中console.log('高度占满,宽度水平居中');initialScale = winHeight / pageHeight;} else {console.log('宽度占满,高度垂直居中');// 高度长了,但是宽度不够// 宽度占满,高度垂直居中initialScale = winWidth / pageWidth;}page.style.width = pageWidth + 'px';page.style.height = pageHeight + 'px';page.style.transform = 'scale(' + initialScale + ')';page.style.transformOrigin = origin;page.style.left = (pageWidth - winWidth) / -2 + 'px';page.style.top = (pageHeight - winHeight) / -2 + 'px';}init();window.addEventListener('resize', init);
.safe-content {/* 布局直接写死成设计稿上的大小 */width: 750px;height: 1334px;position: absolute;left: 0;top: 0;border: 1px solid red;
}.gift-logo {/* 布局直接写死成设计稿上的大小 */width: 679px;height: 703px;background: url('./img/game-title.png') no-repeat center / contain;
}

这时,只需要把safe-contentwitdhheight写死,就能保证宽高比永远保持为750 * 1334

完整页面,点击此处动态缩放整体页面-预览 (手机模式查看)

改变屏幕大小,可以看到safe-content一直都保持正常的宽高比,并且总是宽度占满(宽度比高度小)或者高度占满(高度比宽度小)

完整代码,直接右键源代码

vw和vh

单屏页面布局时,垂直定位尽可能相对高度进行定位,这时可以选择使用百分比或者vh

@use 'sass:math';@function px2vh($px, $height: 1334) {@return math.div($px, $height) * 100 * 1vh;
}

可以封装一个scss方法,将测量得到的px转换成vh

.demo {position: absolute;left: 0;top: 25%; // 垂直定位单位为%top: px2vh(100); // 垂直定位单位为vhwidth: 100%;height: 1rem;
}

如果希望页面的元素在不同的高度下,均能完整展示,可以全部使用vh进行布局

.gift-title {width: 25.86vh;height: 4.72vh;background: url('./img/congratulate.png') no-repeat center / contain;
}.gift-name {font-size: 2.39vh;
}

完整页面,点击此处vh-预览 (手机模式查看)

完整代码,直接右键源代码

总结

前面介绍的5种适配方案,可以总结如下:

1.使用vwvh这两个原生的css3单位,天然支持宽度和高度的适配:对于需要高度适配的元素,使用vh,对于需要宽度适配的元素,使用vw
2.rem相对宽度计算:划分几个高度区域,对于特定的宽高比,单独进行适配。整体页面还是相对宽度进行缩放,只针对部分宽高比,对页面进行特定的样式改动
3.rem动态改变:即可相对宽度,也可相对高度进行计算,此种方案,可以做到保持设计稿的宽高比例,并尽量占满窗口的极致效果
4.不使用rem,写死px,直接js整体缩放页面:此种方案,也可以做到保持设计稿的宽高比例,并尽量占满窗口的极致效果

对于保持设计稿的宽高比例,并尽量占满窗口的效果,可以点击下面的demo进行预览理解:

调整屏幕大小,可以看见页面会上下居中或者左右居中,并且保持宽高比

  • 保持16:9的宽高比-rem动态改变
  • 保持16:9的宽高比-rem动态改变2
  • 保持16:9的宽高比-整体缩放

源码直接右键查看即可

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

不习惯的 Vue3 起步五 のapiHooks 封装相关推荐

  1. 不习惯的 Vue3 起步六 の Echarts绘制下钻地图

    序 看过一些可视化大屏展示地图的例子,准备动手做做. 既然要开始比制作,那么先把目标定好,做一个展示中国城市的下钻地图 使用: Vue3 Vite Typescript echarts 实现效果: 准 ...

  2. Android RecyclerView(五)封装Holder与Adapter(Android 5.0 新特性)

    Android RecyclerView(五)封装Holder与Adapter(Android 5.0 新特性) 1 效果 2 BaseHolder的封装 public class BaseViewH ...

  3. Vue-vben-admin Vue3+TS Axios的封装源码分析

    Vue-vben-admin Vue3+TS Axios的封装源码分析 前言 一.近期再用Vue3+TS 重构之前Vue2的项目,因此想着借鉴一下业界较为优秀的代码,在Git上面找了好久,经过同事推荐 ...

  4. vue3使用echarts并封装echarts组件

    vue3使用echarts并封装echarts组件 前言: 一.安装并导入echart 1.npm下载包 2.配置echarts 二.使用echarts 三.封装echarts为组件 前言: 本文使用 ...

  5. java基础第五篇封装与面向对象

    a.方法: public static void main(String[] args) { } 一般定义标准: 形参:一般把 不确定的量或者变化的量定义在形参位置//圆的的半径,长方形的长和宽,传递 ...

  6. Vue3 + Element-Plus upload组件封装限制上传数量,再次上传则覆盖

    文章目录 一.需求描述 二.实现效果 三.源码 一.需求描述 在 Vue3 项目中使用 Element Plus 封装上传组件.限制上传文件的大小.实现拖拽上传.限制文件只能上传一个,再次上传则覆盖原 ...

  7. vue3 | HighCharts实战自定义封装之径向条形图

    1.前言 目前正在做vue3的数据可视化项目,vue3的组合式api写法十分方便,可以有各种玩法,有兴趣的同学可以看我个人主页的其他文章.难点是在网上找了一圈的有关径向条形图的示例都没有好的解决方案, ...

  8. python接口自动化(三十五)-封装与调用--流程类接口关联(详解)

    简介 流程相关的接口,主要用 session 关联,如果写成函数(如上篇),s 参数每个函数都要带,每个函数多个参数,这时候封装成类会更方便.在这里我们还是以博客园为例,带着小伙伴们实践一下. 接口封 ...

  9. 学习Vue3 第五章(Vue核心虚拟Dom和 diff 算法)

    为什么要学习源码 1.可以提升自己学习更优秀的API设计和代码逻辑 2.面试的时候也会经常问源码相关的东西 3.更快的掌握vue和遇到问题可以定位 介绍虚拟DOM 虚拟DOM就是通过JS来生成一个AS ...

最新文章

  1. mysql send-q_MYSQL---初识
  2. Bi-level error correction for PacBio long reads
  3. python sys.argv 默认值
  4. 百度工程师控制公司服务器“挖矿”:4个月赚10万 判刑3年
  5. apple air装双系统(win7)
  6. WinForm-SuspendLayout、ResumeLayout、PerformLayou——转载
  7. 【转】ORACLE中的子查询 ---OCP--047--46
  8. Django视图(python函数)
  9. Simulink之单管非隔离直流斩波器
  10. 一篇弄懂 HTTP和HTTPS基本关系
  11. Codeforces 432D Prefixes and Suffixes(KMP+dp)
  12. Mac下Boost环境搭建
  13. Linux异常 时间戳 2018-10-08 11:17:22 是未来的 5288025.776562967 秒之后
  14. Android简单实现图片缩略图类ThumbnailUtils
  15. CAS方式实现单点登录
  16. 产品经理,原型设计之前你要做些什么?
  17. 生成带微信头像的用户二维码
  18. 记录各大吃播饭店地址
  19. R7 4800U和i7 9700K 哪个好
  20. python生成随机的大写字母_Python — 随机生成10个大写、小写字母、特殊字符 string模块...

热门文章

  1. STM32F407ZET6+NRF24L01实现一收多发(一发多收)
  2. C 语言的fseek()
  3. 【eclipse】mybatis配置文件创建与mapper接口文件创建
  4. 为什么你成不了数据分析高手?可能是缺少这个思维
  5. WIN10 WPS2019使用LaTeX 100%成功保姆教程
  6. SQL级联表树形(三)
  7. ftp服务器匿名用户文件夹,ftp服务器匿名用户文件夹
  8. 在宜家兼职收银员创收
  9. 刮刮彩票 (20 分)
  10. SQL Server菜鸟入门