不习惯的 Vue3 起步五 のapiHooks 封装

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

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

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

设备独立像素 (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个物理像素

不习惯的 Vue3 起步五 のapiHooks 封装_第1张图片

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客户端)有所改变。

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

安全区 + 长背景图

不习惯的 Vue3 起步五 のapiHooks 封装_第2张图片

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

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

不习惯的 Vue3 起步五 のapiHooks 封装_第3张图片

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

 

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

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

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

点击展开源代码```

Document
深红

水漾烛光礼盒

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

krramel沐浴套装*1

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

填写领取信息

```

宽高比

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

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

不习惯的 Vue3 起步五 のapiHooks 封装_第4张图片

如果此时,父级元素还设置了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的相关知识,提升工作效率。



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

你可能感兴趣的:(vue.js,javascript,前端,uni-app)