移动端适配就是在进行屏幕宽度的等比例缩放
文中我强调了移动端适配是对屏幕宽度进行缩放:对于普通的流式布局(长屏幕页面),页面内容是可以上下滚动的。屏幕小,一屏幕看到的东西虽然变少,但是用户可以通过手势滚动页面,继续浏览下一屏的内容。因此在常规情况下,对于屏幕宽度进行等比例缩放已经能解决大部分应用场景了
但是对于一种特殊的场景:单屏页面(又称翻屏页面),由于需要把一整屏的内容完整展示给用户,同时又要求页面不能出现滚动条,那么,仅仅只是针对屏幕宽度进行等比例缩放的适配,其实效果并不理想
设备独立像素是一种可以被程序所控制的虚拟像素,在Web开发中对应CSS像素
以iphone7
举例:
iphone7
的设备独立像素
为 375 * 667
也就是手机全屏下的大小,同时也是chrome
模拟器展示的尺寸
可以通过js的screen.width
和screen.height
获取
设备像素也可以叫物理像素,由设备的屏幕决定,其实就是屏幕中控制显示的最小单位
以iphone7
举例:
iphone7
的设备像素
为 750 * 1334
750 * 1334
这个尺寸也可以称为设计像素
,我们设计和开发页面时,就是以这个设计像素为准
设备像素比(dpr) = 设备像素 / 设备独立像素
以iphone7
举例:
iphone7
的dpr = 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
)上下居中作为整个网页的背景
之后,我们把页面所有的内容,相对safe-content
进行布局
完整页面,点击此处预览 (手机模式查看)
完整代码,直接右键源代码或者点击下方按钮展开
点击展开源代码```
Document水漾烛光礼盒
蒂普提克香氛蜡烛(70g)*1
krramel沐浴套装*1
请到游戏内【精彩活动-实物周边奖励兑换】
填写领取信息
上面的方案,对于移动端(屏幕高度大于屏幕宽度)的大部分场景,的确够用了。
但是在折叠屏手机(屏幕宽度和高度差别不大),ipad,pc端(屏幕高度小于屏幕宽度)的设备下,我们的页面就很有可能超出了完整的一屏。
如果此时,父级元素还设置了overflow: hidden;
,那么用户甚至不能滑动查看超出屏幕的内容,如果底部是一个可交互的按钮,那么用户就永远不能触发之后的流程了!
我们的rem
适配方案,是相对于屏幕宽度进行缩放的,但是不同机型的手机,可视区域的宽高比并不固定,因此对于部分手机,页面内容就很有可能出现超出屏幕底部或者底部留有空白。
对于底部留有空白,一般发生在可视高度比可视宽度大很多的情况,前面介绍的安全区 + 长背景图
方案,就是针对此种情况的解决方案。
而对于超出屏幕底部,一般发生在可视高度和可视宽度相差不大(折叠屏手机),甚至可视高度比可视宽度小(横屏或者pc端)的情况,解决方案一般如下:
css
进行宽高比判断js
进行宽高比判断js
动态修改rem
大小js
动态缩放整体页面vw
和vh
进行布局注意和device-aspect-ratio
进行区分,device-aspect-ratio
是和设备尺寸进行绑定的,但是我们之前介绍过:网页的可视区域会随着访问方式(浏览器,APP客户端)有所改变,因此aspect-ratio
才是我们真正需要的属性。
aspect-ratio 定义输出设备中的页面可见区域宽度与高度的比率
同时它有两个max-aspect-ratio
和min-aspect-ratio
兄弟属性,可以和max-width
和min-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
,我们可以这样进行高度划分:
安全区 + 长背景图
@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-预览 (手机模式查看)
完整代码,直接右键源代码
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
的参考对象,让它根据可视高度进行计算
我们甚至可以实现:无论窗口怎么变,我们的内容都保持原来的比例,并尽量占满窗口
封装成一个通用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-content
的witdh
和height
写死,就能保证宽高比永远保持为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-content
的witdh
和height
写死,就能保证宽高比永远保持为750 * 1334
完整页面,点击此处动态缩放整体页面-预览 (手机模式查看)
改变屏幕大小,可以看到safe-content
一直都保持正常的宽高比,并且总是宽度占满(宽度比高度小)或者高度占满(高度比宽度小)
完整代码,直接右键源代码
单屏页面布局时,垂直定位尽可能相对高度进行定位,这时可以选择使用百分比或者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.使用vw
和vh
这两个原生的css3
单位,天然支持宽度和高度的适配:对于需要高度适配的元素,使用vh
,对于需要宽度适配的元素,使用vw
2.rem
相对宽度计算:划分几个高度区域,对于特定的宽高比,单独进行适配。整体页面还是相对宽度进行缩放,只针对部分宽高比,对页面进行特定的样式改动
3.rem
动态改变:即可相对宽度,也可相对高度进行计算,此种方案,可以做到保持设计稿的宽高比例,并尽量占满窗口的极致效果
4.不使用rem
,写死px
,直接js
整体缩放页面:此种方案,也可以做到保持设计稿的宽高比例,并尽量占满窗口的极致效果
对于保持设计稿的宽高比例,并尽量占满窗口的效果,可以点击下面的demo进行预览理解:
调整屏幕大小,可以看见页面会上下居中或者左右居中,并且保持宽高比
源码直接右键查看即可
最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。
有需要的小伙伴,可以点击下方卡片领取,无偿分享