前言
了解大屏适配之前,首先先要了解一下屏幕尺寸、分辨率以及设备像素和逻辑像素。
屏幕尺寸
以电脑为例,我们经常听到屏幕尺寸为24寸,27寸等等,这到底怎么计算的呢,是面积吗?
很多小伙伴觉得指的是屏幕面积 -寸为面积单位,那你就大错特错了啦!寸其实是英寸为长度单位,而上面这些其实是屏幕对角线的长度。
屏幕是长方形的,当我们知道屏幕的长和宽的时候,利用勾股定理就可以算出对角线啦!
补充:1英寸(inch)=2.54厘米(cm)
像素
目前主流的画面显示是通过一个个小色块拼凑起来显示完整的图像的,而所谓的像素说的就是这些小色块,这些小色块为图像显示的最小单位。
定义: 在由一个数字序列表示的图像中的一个最小单位。可以将像素视为整个图像中不可分割的单位或者是元素。不可分割的意思是它不能够再切割成更小单位亦或是元素,它是以一个单一颜色的小格存在。每一个点阵图像包含了一定量的像素,这些像素决定图像在屏幕上所呈现的大小。
分辨率(点密度)
屏幕分辨率代表着像素个数,2K屏幕,其实就是2560px1440px,可以简单理解为水平有2560个像素,垂直有1440个像素。1080p表示的是1920px1080px的分辨率,720p表示的是1280*720的分辨率。单位面积像素点越多屏幕显示就越清晰和细腻。可见图片像素个数越多,尺寸越小,图片就越清晰和细腻,即点密度越大(PPI)。
而对于一张图来说,图片文件本身没有物理概念上的尺寸大小,因此描述图片文件时,尺寸等同于分辨率。当然均是以像素为单位。
像素虽高,若印的照片也很大,其“分辨率”并不高,照片照样也不细腻。相反,像素不高,若只印很小幅面的照片,也可以得到很细腻的照片。确切地说,像素高,意味着能拍出幅面大的照片——像素越高,照片的尺寸必然越大。所以,“像素”的高低,表示着照片幅面的大小
分辨率比
经过研究发现人眼最适合的长宽比为16:9,即黄金比例。之前常见的比例为4:3。1920*1080为分辨率比为16:9。
像素、分辨率与实际尺寸之间的转换关系
分辨率计算以其在长度方向上的像素数,除以长度的尺寸数(英寸)。或以其在宽度方向的像素数,除以宽度的尺寸数(英寸)。
例如以图片的像素640×480,其尺寸大小是:长:3.556寸,宽:2.667寸;该图片的分辨率即为640÷3.556=180(像素/英寸),或480÷2.667=180(像素/英寸);又如大多数网页制作常用图片分辨率为72,即每英寸像素为72,1英寸等于2.54厘米,那么通过换算可以得出每厘米等于28像素;又如15x15厘米长度的图片,等于420*420像素的长度。
当图片尺寸以像素为单位时,需要指定其固定的分辨率,才能将图片尺寸与现实中的实际尺寸相互转换。如相机的像素标称为1600×1200,实际像素为1600×1200=1920000≈2000000,就是200万(像素)
物理像素和逻辑像素的关系与联系
像素是图像的基本采样单位,它不是一个确定的物理量,也不是一个具体的点或小方块(尽管可以用点或小方块来呈现),而是一个抽象概念。
上述屏幕分辨率如1920px*1080px这类像素其实是物理像素,它们在指定的屏幕尺寸上可以转化为真实的公制面积如(cm2)。其中点距离可以表示为物理像素。
如下图,一些常见的液晶显示屏点距
| 尺寸 | 规格 | 点距 |
|---|---|---|
| 15.6英寸 | 16:9宽屏(1366×768) | 0.252mm×0.252mm |
| 17英寸 | 5:4普屏(1280×1024) | 0.264mm×0.264mm |
| 17英寸 | 16:9宽屏(1366×768) | 0.252mm×0.252mm |
| 17英寸 | 16:10宽屏(1440×900) | 0.255mm×0.255mm |
| 17英寸 | 5:4普屏(1280×1024) | 0.264mm×0.264mm |
| 18.5英寸 | 16:9宽屏(1366×768) | 0.300mm×0.300mm |
| 19英寸 | 16:10宽屏(1440×900) | 0.285mm×0.285mm |
| 19英寸 | 16:10宽屏(1680×1050) | 0.244mm×0.244mm |
| 19英寸 | 5:4普屏(1280×1024) | 0.294mm×0.294mm |
| 20英寸 | 5:4普屏(1400×1050) | 0.292mm×0.292mm |
| 20英寸 | 5:4普屏(1600×1200) | 0.255mm×0.255mm |
| 20英寸 | 16:9宽屏(1600×900) | 0.276mm×0.276mm |
| 20英寸 | 16:10宽屏(1680×1050) | 0.258mm×0.258mm |
| 21.5英寸 | 16:9宽屏(1920×1080) | 0.248mm×0.248mm |
| 21.6英寸 | 16:10宽屏(1680×1050) | 0.276mm×0.276mm |
| 22英寸 | 16:10宽屏(1680×105) | 0.282mm×0.282mm |
| 23英寸 | 16:9宽屏(1920×1080) | 0.266mm×0.266mm |
| 23英寸 | 16:9宽屏(2048×1152) | 0.249mm×0.249mm |
| 23英寸 | 16:10宽屏(1920×1200) | 0.258mm×0.258mm |
| 24英寸 | 16:9宽屏(1920×1080) | 0.276mm×0.276mm |
| 24英寸 | 16:10宽屏(1920×1200) | 0.27mm×0.27mm |
| 25.5英寸 | 16:10宽屏(1920×1200) | 0.2865mm×0.2865mm |
| 27英寸 | 16:10宽屏(1920×1200) | 0.303mm×0.303mm |
| 30英寸 | 16:10宽屏(2560×1600) | 0.2505mm×0.2505mm |
关于像素的几个概念:
设备像素: 设备的物理像素,其尺寸大小是绝对的。
逻辑像素: CSS 的像素单位,其尺寸大小是相对的,也称为独立像素。(其中MDN中把px定为了绝对单位,长度为1th/96in)
DPI(dots per inch): 像素密度,表示水平或垂直方向每英寸长度的像素数目。
PPI(pixels per inch): 像素密度,表示沿对角线每英寸长度的像素数目。
缩放因子(Scale Factor): 逻辑像素相对于设备像素的放大比例,可通过 window.devicePixelRatio 获得,但二者并不完全等同。
设备像素 = 逻辑像素 * 缩放因子
设备像素都是固定的,所以逻辑像素大小由缩放因子决定。对于桌面设备,逻辑像素通常就等同于物理像素,本来是不用考虑缩放问题的。而现在屏幕变得越来越高清,PPI 越来越大,如果没有缩放,所有的东西看起来都会比较小,因此需要放大。所以决定缩放因子大小的,就是像素密度,密度越大、越高清的屏幕,需要的缩放比例就越大。()
PC 上的缩放比例是自定义的,而移动端的缩放比例是通过 viewport 确定的,viewport 就是屏幕那块固定的可视区域。默认情况下,移动端浏览器会将 viewport 宽度设为980px(也有可能是1024px 或其它值),也就是说1px = 设备屏幕宽度的1/980。这跟缩放因子没有任何关系。这时的1px 非常小,所有的元素都变得非常小,移动端浏览器之所以这么做,是为了尽可能完整的显示 PC 端的网页,然后允许用户通过缩放来查看细节。
如果我们不希望采用默认的设置,就需要人为设置
viewport:<meta name=“viewport” content=“width=device-width”>
将 viewport 宽度设为设备宽度,就跟缩放因子有关了。比如:iphone6(750 × 1334)的 PPI 是326,缩放因子是2,所以1个逻辑像素的大小等于2个设备像素。对于 iphone6,1px = 屏幕宽度的1/375,相比1/980放大了不少,而这时候的1px 就是一个比较理想的大小,即比较符合我们在 PC 端使用 px 时的感受。(同样的逻辑像素(css-px)在屏幕尺寸小像素大所占的设备像素也就越大,所以屏幕小的显示的内容也不会很小了。)
几种适配方案
1.PPI适配
无论是什么尺寸的屏幕,希望显示的内容都是相同大小的。
如文字,希望 16px 的文字无论在什么样的屏幕下看起来都是一样大的。这里的逻辑像素 - px 是一个实际物理尺寸固定的单位。
设置 viewport 就可以实现这个目的:<meta name=“viewport” content=“width=device-width”>
1个逻辑像素的尺寸 = 1 / PPI × 缩放因子,单位为英寸,所以说设置 viewport 本质上是把 px 变成了一个“绝对 单位”。
2.分辨率适配
解决找一个相对单位,使同一尺寸在不同大小的屏幕上看上去相对大小一致的问题(百分比)。
如一张宽100%,高100unit 的 banner 图,希望它在任何大小的屏幕上能够等比例缩放,因此需要这里的 unit 是一个相对单位。vw 和 vh 就是很好的相对单位。
根据设计稿的尺寸将px按比例转化为vw vh
假设设计稿尺寸为 1920*1080
即:
网页宽度=1920px
网页高度=1080px
我们都知道
网页宽度=100vw
网页宽度=100vh
所以,在 1920px*1080px 的屏幕分辨率下
1920px = 100vw
1080px = 100vh
以一个宽 300px 和 200px 的 div 来说,其所占的宽高,以 vw 和 vh 为单位,计算方式如下:
vwDiv = (300px / 1920px ) * 100vw
vhDiv = (200px / 1080px ) * 100vh
所以,就在 1920*1080 的屏幕分辨率下,计算出了单个 div 的宽高
当屏幕放大或者缩小时,div 还是以 vw 和 vh 作为宽高的,就会自动适应不同分辨率的屏幕
代码如下
// 使用 scss 的 math 函数,https://sass-lang.com/documentation/breaking-changes/slash-div
@use "sass:math";
// 默认设计稿的宽度
$designWidth: 1920;
// 默认设计稿的高度
$designHeight: 1080;
// px 转为 vw 的函数
@function vw($px) {
@return math.div($px, $designWidth) * 100vw;
}
// px 转为 vh 的函数
@function vh($px) {
@return math.div($px, $designHeight) * 100vh;
}
路径配置
只需在vue.config.js里配置一下utils.scss的路径,就可以全局使用了
vue.config.js
js
复制代码
const path = require("path");
function resolve(dir) {
return path.join(__dirname, dir);
}
module.exports = {
publicPath: "",
configureWebpack: {
name: "app name",
resolve: {
alias: {
"@": resolve("src"),
},
},
},
css: {
// 全局配置 utils.scs,详细配置参考 vue-cli 官网
loaderOptions: {
sass: {
prependData: `@import "@/styles/utils.scss";`,
},
},
},
};
在 .vue 中使用
js
复制代码
<template>
<div class="box">
</div>
</template>
<script>
export default{
name: "Box",
}
</script>
<style lang="scss" scoped="scoped">
/*
直接使用 vw 和 vh 函数,将像素值传进去,得到的就是具体的 vw vh 单位
*/
.box{
width: vw(300);
height: vh(100);
font-size: vh(16);
background-color: black;
margin-left: vw(10);
margin-top: vh(10);
border: vh(2) solid red;
}
</style>
3.DPR(devicePixelRatio)适配
解决在设置了 viewport,width=device-width 的情况下,如何画出1px(设备像素)的问题。
DPR = 2 意味着 CSS 中的1px 会用2个设备像素来渲染,解决方案有:用小数、渐变、transform 缩放,手机淘宝的做法是使用 js 动态设置 viewport 的 initial-scale。
scale--通过 css 的 scale 属性,根据屏幕大小,对图表进行整体的等比缩放,从而达到自适应效果
上代码
import { ref } from 'vue'
//尺寸适应hooks
export default function useDraw() {
// * 指向最外层容器
const appRef = ref(null)
// * 定时函数
const timer = ref(0)
// * 默认缩放值
const scale = {
width: '1',
height: '1',
}
// * 设计稿尺寸(px)
const baseWidth = 1920
const baseHeight = 1080
// * 需保持的比例(默认1.77778)
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
const calcRate = () => {
// 当前宽高比
const currentRate = parseFloat((window.innerWidth / window.innerHeight).toFixed(5))
if (appRef.value) {
if (currentRate > baseProportion) {
// 表示更宽
scale.width = ((window.innerHeight * baseProportion) / baseWidth).toFixed(5)
scale.height = (window.innerHeight / baseHeight).toFixed(5)
appRef.value.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`
} else {
// 表示更高
scale.height = ((window.innerWidth / baseProportion) / baseHeight).toFixed(5)
scale.width = (window.innerWidth / baseWidth).toFixed(5)
appRef.value.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`
}
}
}
const resize = () => {
timer.value&&clearTimeout(timer.value)
timer.value = setTimeout(() => {
calcRate()
}, 200)
}
// 改变窗口大小重新绘制 ---resize 窗口发生改变触发
const windowDraw = () => {
window.addEventListener('resize', resize)
}
// 改变窗口大小重新绘制
const unWindowDraw = () => {
window.removeEventListener('resize', resize)
}
return {
appRef,
calcRate,
windowDraw,
unWindowDraw
}
}