本文主要记录适配移动端以及多屏幕的解决办法,还有postcss转换插件的编写。
在MDN中提到:
在移动设备和其他窄屏设备中,某些内容在比普通屏幕更宽的虚拟窗口或视口中渲染页面,然后缩小渲染的结果,以便可以一次看到所有内容。然后,用户可以通过平移和缩放以查看页面的不同区域。例如,如果移动屏幕的宽度为 640px,则页面可能会使用 980px 的虚拟视口渲染,然后页面将缩小以适应 640px 的空间。
换成通俗易懂的话就是说,为了适配窄屏,他会把渲染视口以短边进行截断,然后就出现了横向滚动条,让用户能够左右滑动来查看完整的页面,但是这样做对移动端有很大的问题,左右滑动对移动端的用户体验大打折扣。
所以需要某种机制或者方法来适配移动端。
以下是最为常见的移动端适配方案。
meta标签写在标签中
<head>
<meta charset="UTF-8">
<!-- 移动端适配-->
<meta name="viewport" content="with=device-width,initial-scale=1.0">
<title>Title</title>
</head>
其中有很多选项用来调整视口与屏幕的关系
width
控制视口的大小。这可以设置为特定像素数(如’width=600’),也可以设置为特殊值device-width,即 100vw,100% 的视口宽度。最小值为 1。最大值为 10000。负值会被忽略。
height
控制视口的大小。这可以设置为特定像素数(如 width=600),也可以设置为特殊值 device-height,即 100vh,100% 的视口高度。最小值为 1。最大值为 10000。负值会被忽略。
initial-scale
控制页面首次加载时显示的缩放倍数。最小值是 0.1。最大值是 10。默认值为 1。负值会被忽略。
minimum-scale
控制页面允许缩小的倍数。最小值是 0.1。最大值是 10。默认值为 1。负值会被忽略。
maximum-scale
控制页面允许放大的倍数。设置一个低于 3 的值将不具备无障碍访问性。最小值是 0.1。最大值是 10。默认值为 1。负值会被忽略。
user-scalable
控制是否允许页面上的放大和缩小操作。有效值为 0、1、yes 或 no。默认值为 1,与 yes 相同。将值设置为 0(即与 no 相同)将违反 Web 内容无障碍指南(WCAG)。
interactive-widget
指定交互式 UI 组件(如虚拟键盘)对页面视口的影响。有效值:resizes-visual、resizes-content 或 overlays-content。默认值:resizes-visual。
viewport-fit
设置为 cover 可以解决 刘海屏 的留白问题
最终完整的meta标签可以写成:
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
在创建vue项目时,他会在index.html中自动加入这个标签。
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
优点:
缺点:
rem是个长度单位,表示相对根元素的字体大小,例如根元素为14px,那么1rem表示14px,2rem表示28px。
利用JS控制根元素的字体大小就可以控制整个页面的字体大小的适配
首先是需要获取当前视口宽,利用宽动态调整根元素字体大小document.documentElement.clientWidth
。
利用window.onresize
监听当前窗口变化
其中关键点是以下的两个点:
计算对应宽度的根元素字体大小
这个一般设计稿会给,设计稿都是固定某个宽,上边对应的字体大小就是这个宽对应的字体大小,就比如设计稿给我们一个宽375px,字体28px的设计稿,那么其他宽度对应的字体大小就是:其他宽度 × 28 ÷ 750
计算子元素字体与根元素对应关系
这个也是根据设计稿计算个比例就可以了,例如一个根元素是28px,子元素是14px,那么转换成rem就是:14 ÷ 28 = 0.5 rem
这个也有一些插件可以实现将px自动转换成rem,如postcss-pxtorem,参考文章:px自动转rem
<style>
.box{
font-size: 3rem;
}
#text{
font-size: 0.5rem;
}
</style>
<body>
<div class="box">
smz
</div>
<div id="text"></div>
</body>
<script>
function adapter() {
const text = document.querySelector('#text')
//获取布局视口宽度,布局视口=设备横向独立像素值
const dpWidth = document.documentElement.clientWidth
//计算根字体大小
const rootFonstSize = (dpWidth * 28)/375
//设置根字体大小
document.documentElement.style.fontSize = rootFonstSize + 'px'
text.innerText = `当前根元素字体大小:${document.documentElement.style.fontSize}`
}
adapter()
// 监听窗口变化
window.onresize = adapter
</script>
vw和vh也是长度单位,他与传统的以像素为单位不同,他是以视口百分比为单位
对于百分比布局有一定的区别,百分比是相对于父元素,尔vw和vh是相对于视口的
也就是说100vw和100vh表示整个视口
使用vw和vh就不用再使用JS控制了
同样,使用此单位也需要计算设计稿中px与视口比例关系:对应像素长度 × 100vw ÷ 设计稿屏幕像素宽
less文件:
body{
@basic:375/100vw;
.box{
width: 300/@basic;
height: 150/@basic;
background: red;
margin: auto;
}
}
在vite开发的项目中集成了postcss,可以利用postcss编写一个全局转换的的一个方法,可以快速将px转换成vw,或者对某些可以做到不转换。
import {Plugin} from "postcss";
// 默认宽度
const Options = {
viewportWidth: 375 //UI设计稿宽度
}
interface Options {
viewportWidth?:number
}
export const PostcssPxToViewport = (options:Options = Options):Plugin =>{
const opt = Object.assign({},Options,options)// 将Options,options复制到同一对象中
return {
postcssPlugin : "postcss-px-to-viewport",// 名称
//钩子函数 取css结点
Declaration(cssNode){
// 这里可以改名字‘smz’ 改成需要转换的,比如‘smz’ 就只针对 单位为'smz'的css结点进行转换
if (cssNode.value.includes('smz')){// cssNode.value.includes('px')对含有px单位的css结点,可更改
const num = parseFloat(cssNode.value)// 转换成数字去掉px
cssNode.value = `${(( num / opt.viewportWidth) * 100).toFixed(2)}vw` // 计算公式
}
}
}
}
使用:
<template>
<div class="box">
postcss转换插件
</div>
<div class="box2">
普通未转换
</div>
</template>
<style scoped>
.box{
width: 200smz;
height: 100smz;
font-size: 20smz;
border: 3px solid red;
}
.box2{
width: 200px;
height: 100px;
font-size: 20px;
border: 3px solid yellow;
}
</style>
以上三种方案,是相互搭配进行移动端的适配的。