共计 12224 个字符,预计需要花费 31 分钟才能阅读完成。
前文《挪动端常见适配计划》中,我介绍了挪动端的一些常见适配计划
挪动端适配就是在进行 屏幕宽度 的等比例缩放
文中我强调了挪动端适配是对 屏幕宽度 进行缩放:对于一般的流式布局(长屏幕页面),页面内容是能够高低滚动的。屏幕小,一屏幕看到的货色尽管变少,然而用户能够通过手势滚动页面,持续浏览下一屏的内容。因而在惯例状况下,对于屏幕宽度进行等比例缩放曾经能解决大部分利用场景了
然而对于一种非凡的场景:单屏页面(又称翻屏页面),因为须要把 一整屏 的内容 残缺 展现给用户,同时又要求页面不能呈现滚动条,那么,仅仅只是针对屏幕宽度进行等比例缩放的适配,其实成果并不现实
设施独立像素 (CSS 像素)
设施独立像素是一种能够被程序所管制的虚构像素,在 Web 开发中对应 CSS 像素
以 iphone7
举例:
iphone7
的 设施独立像素
为 375 * 667
也就是手机全屏下的大小,同时也是 chrome
模拟器展现的尺寸
能够通过 js 的 screen.width
和screen.height
获取
设施像素 (物理像素)
设施像素也能够叫物理像素,由设施的屏幕决定,其实就是屏幕中管制显示的最小单位
以 iphone7
举例:
iphone7
的 设施像素
为 750 * 1334
750 * 1334
这个尺寸也能够称为 设计像素
,咱们设计和开发页面时,就是以这个设计像素为准
设施像素比(DPR)
设施像素比(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
) 高低居中作为整个网页的背景
<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
进行布局
残缺页面,点击此处预览 (手机模式查看)
残缺代码,间接右键源代码
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Document</title>
<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%);
/* border: 1px solid red; */
}
.go-back {
width: 0.91rem;
height: 0.92rem;
background: url('./img/back.png') no-repeat center / contain;
position: absolute;
left: 0.14rem;
top: 0.15rem;
}
.rule-btn {
width: 0.83rem;
height: 0.83rem;
background: url('./img/rule-btn.png') no-repeat center / contain;
position: absolute;
left: 1.27rem;
top: 0.20rem;
}
.username {
min-width: 1.67rem;
height: 0.41rem;
line-height: 0.41rem;
background: url('./img/user-bg.png') no-repeat center / cover;
position: absolute;
right: 0;
top: 0.25rem;
color: #fcfcfc;
font-size: 0.22rem;
text-align: right;
padding-right: 0.13rem;
padding-left: 0.48rem;
}
.gift-container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
}
.gift-logo {
width: 6.79rem;
height: 7.03rem;
background: url('./img/game-title.png') no-repeat center / contain;
}
.gift-title {
width: 3.45rem;
height: 0.63rem;
background: url('./img/congratulate.png') no-repeat center / contain;
}
.gift-content {
margin-top: -1.8rem;
display: flex;
flex-direction: column;
align-items: center;
}
.gift-name {
font-size: 0.3216rem;
line-height: 0.24rem;
letter-spacing: 0.032rem;
color: #bd5874;
text-align: center;
margin-top: 0.2rem;
text-shadow: 0 0 5px #fff, 0 0 5px #fff;
}
.gift-icon {
margin: 0.4rem 0 0.5rem;
width: 1.94rem;
height: 1.65rem;
background: url('./img/gift-1.png') no-repeat center / contain;
}
.gift-desc {
font-size: 0.2rem;
line-height: 0.2426rem;
letter-spacing: 0.019rem;
color: #90949e;
text-align: center;
margin-bottom: 0.2rem;
text-shadow: 0 0 5px #fff, 0 0 5px #fff;
}
.gift-get-info {
font-size: 0.23rem;
line-height: 0.30rem;
color: #d16b88;
text-align: center;
text-shadow: 0 0 5px #fff, 0 0 5px #fff;
}
</style>
</head>
<body>
<div id='app'>
<div class="safe-content">
<div class="go-back"></div>
<div class="rule-btn"></div>
<div class="username"> 深红 </div>
<div class="gift-container">
<div class="gift-logo"></div>
<div class="gift-content">
<div class="gift-title"></div>
<p class="gift-name"> 水漾烛光礼盒 </p>
<div class="gift-icon"></div>
<div class="gift-desc">
<p> 蒂普提克香氛蜡烛(70g)*1</p>
<p>krramel 沐浴套装 *1</p>
</div>
<div class="gift-get-info">
<p> 请到游戏内【精彩流动 - 实物周边处分兑换】</p>
<p> 填写支付信息 </p>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
宽高比
下面的计划,对于挪动端 ( 屏幕高度大于屏幕宽度 ) 的大部分场景,确实够用了。
然而在折叠屏手机 ( 屏幕宽度和高度差异不大 ),ipad,pc 端( 屏幕高度小于屏幕宽度 ) 的设施下,咱们的页面就很有可能超出了残缺的一屏。
如果此时,父级元素还设置了overflow: hidden;
,那么用户甚至不能滑动查看超出屏幕的内容,如果底部是一个可交互的按钮,那么用户就永远不能触发之后的流程了!
问题起因
咱们的 rem
适配计划,是绝对于屏幕宽度进行缩放的,然而不同机型的手机,可视区域的宽高比并不固定,因而对于局部手机,页面内容就很有可能呈现 超出屏幕底部 或者 底部留有空白。
对于 底部留有空白 ,个别产生在可视高度比可视宽度大很多的状况,后面介绍的 安全区 + 长背景图
计划,就是针对此种状况的解决方案。
而对于 超出屏幕底部 ,个别产生在可视高度和可视宽度相差不大(折叠屏手机),甚至可视高度比可视宽度小(横屏或者 pc 端) 的状况,解决方案个别如下:
- 应用
css
进行宽高比判断 - 应用
js
进行宽高比判断 - 应用
js
动静批改rem
大小 - 应用
js
动静缩放整体页面 - 应用
vw
和vh
进行布局
aspect-ratio
留神和 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
,咱们能够这样进行高度划分:
- 高度大于 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/pc
console.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.documentElement
function 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
始终都放弃失常的宽高比,并且总是宽度占满 (宽度比高度小) 或者高度占满(高度比宽度小)
残缺代码,间接右键源代码
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); // 垂直定位单位为 vh
width: 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 种适配计划,能够总结如下:
- 应用
vw
和vh
这两个原生的css3
单位,人造反对宽度和高度的适配:对于须要高度适配的元素,应用vh
,对于须要宽度适配的元素,应用vw
rem
绝对宽度计算:划分几个高度区域,对于特定的宽高比,独自进行适配。整体页面还是绝对宽度进行缩放,只针对局部宽高比,对页面进行特定的款式改变rem
动静扭转:即可绝对宽度,也可相对高度进行计算,此种计划,能够做到 放弃设计稿的宽高比例,并尽量占满窗口 的极致成果- 不应用
rem
,写死px
,间接js
整体缩放页面:此种计划,也能够做到 放弃设计稿的宽高比例,并尽量占满窗口 的极致成果
对于 放弃设计稿的宽高比例,并尽量占满窗口 的成果,能够点击上面的 demo 进行预览了解:
调整屏幕大小,能够看见页面会上下居中或者左右居中,并且放弃宽高比
- 放弃 16:9 的宽高比 -rem 动静扭转
- 放弃 16:9 的宽高比 -rem 动静扭转 2
-
放弃 16:9 的宽高比 - 整体缩放
参考
- 对于挪动端适配,你必须要晓得的
- 不要再问我挪动适配的问题了
- device-aspect-ratio 与 aspect-ratio 单屏布局
- 翻页 H5 全分辨率适配最佳实际
- 大屏上的全屏页面的自适应适配计划
- 挪动端单屏解决方案
- 挪动端单屏解决方案(续)
- 如何打造一个高效适配的 H5
- 全屏 HTML5 适配个人见解
- 通过 rem 布局 +media-query:aspect-ratio 实现挪动端全机型适配笼罩