Skip to main content

006-不同尺寸终端的自动适配

· 11 min read
tjuzyp

本系列文章适用于前端开发初学者,我将从环境搭建开始,逐步介绍如何开发一个基于 Vue 3.x & Ant Design Vue 4.x 的网站,如果哪位朋友发现其中存在技术错误或其他不当之处,请及时指出,如果有任何疑问或需要帮助,也可以在评论区留言,我将尽我所能一一解答,谢谢。

本篇文章将介绍关于不同尺寸终端的自动适配,由于终端尺寸复杂多样,而且设备/系统/应用兼容性存在差异,导致适配工作经常百密一疏(其实往往是一密百疏),所以它一直是前端开发最头疼的问题之一。

为确保网站、应用或其他界面在各种设备上都能够良好的呈现和使用,开发人员经常使用以下方法进行适配:

  1. 流式布局:它使用百分比单位和自适应容器来创建流式布局,使页面元素根据屏幕尺寸自动调整大小
  2. 弹性布局:它使用相对单位(如百分比、vw、em、rem 等)来定义元素的相对尺寸,以便元素可以根据父容器的大小进行伸缩
  3. 移动优先:首先考虑移动设备的设计风格及操作交互,然后再尽量适配大屏幕
  4. 使用 Viewport 参数控制页面在移动设备上的缩放显示
  5. 使用第三方组件库:例如 Vant、Mint UI 等,这些组件库已经做好了移动端大部分的适配工作

在具体的实现上,以上方法一般会组合使用,原本设计好了统一的风格,但它不能百分百适配所有情况,只能发现一个问题解决一个问题,结果就像打补丁,补丁接补丁、补丁摞补丁。

本篇文章我将结合三国史诗馆网站的终端适配进行介绍,包括以下部分:

  1. 常用配置/元素/单位的基本概念
  2. 三国史诗馆网站 PC 端与手机端的显示差异
  3. 主要代码改动

常用配置/元素/单位的基本概念

下方表格列举了一些比较常见的适配相关的前端名词:

名词解释使用示例
width/height定义元素的宽度/高度style="width:800px;height:600px;"
scrollWidth/scrollHeight对象的内容的实际宽度/高度let scrollWidth = document.body.scrollWidth;
clientWidth/clientHeight对象的内容的可视宽度/高度let clientWidth = document.body.clientWidth;
offsetWidth/offsetHeight对象的整体的可视宽度/高度(包括滚动条等)let offsetWidth = document.body.offsetWidth;
innerWidth/innerHeight浏览器窗口的内部宽度/高度let innerWidth = document.body.innerWidth;
font-size字体大小style="font-size:12px;"
px以像素为单位定义元素大小style="font-size:12px;"
pt以磅为单位定义元素大小style="font-size:12pt;"
vw相对于视口宽度(Viewport Width)的百分比style="width:10vw;" // 相当于视窗宽度的 10%
vh相对于视口高度(Viewport Width)的百分比style="width:10vh;" // 相当于视窗高度的 10%
em相对于当前元素字体大小的相对长度单位style="width:10em;"
rem相对于根元素字体大小的相对长度单位style="width:10rem;"
%相对于父元素的百分比值style="width:10%;"
@media媒体查询:根据不同的媒体类型和条件来区分各种设备media="screen" 或 @media screen
calc()一个可以进行数值基本运算的函数height:calc(100vh-64px);
span栅格布局中的栅格占位格数span="8" // 元素占父元素的 8/24 栅格
xs视窗宽度 <576px 时栅格占位格数xs="8" // 视窗宽度 <576px 时元素占父元素的 8/24 栅格
sm视窗宽度 ≥576px 时栅格占位格数sm="8" // 视窗宽度 ≥576px 时元素占父元素的 8/24 栅格
md视窗宽度 ≥768px 时栅格占位格数md="8" // 视窗宽度 ≥768px 时元素占父元素的 8/24 栅格
lg视窗宽度 ≥992px 时栅格占位格数lg="8" // 视窗宽度 ≥992px 时元素占父元素的 8/24 栅格
xl视窗宽度 ≥1200px 时栅格占位格数xl="8" // 视窗宽度 ≥1200px 时元素占父元素的 8/24 栅格
xxl视窗宽度 ≥1600px 时栅格占位格数xxl="8" // 视窗宽度 ≥1600px 时元素占父元素的 8/24 栅格

除常见名词外,其中我主要用到了以下几个:

  1. offsetWidth:使用它作为判断尺寸大小的依据
  2. vw:使用百分比宽度设置页面元素的 padding/margin,可以使元素间距在大小屏幕上都很协调
  3. vh:主要用于设置页面的最小高度,避免在宽高比不协调的设备中显示异常
  4. em:主要用于设置文字的字号与间距等
  5. calc():用于计算页面最小高度

显示差异及代码实现

整体布局

整体上主要是页面边距的差异,将在人物大全页面中进行说明。

在页面入口文件 App.vue 中获取页面可视宽度,将其存储到全局变量中,用于其他页面的样式选择判断,实现代码如下:

/src/App.vue
const storage = useStore();
// 更新页面宽度
const onResize = () => {
storage.commit('updateClientWidth', document.body.offsetWidth);
};

// 加载页面
onMounted(() => {
// 更新页面宽度
onResize();
window.addEventListener("resize", onResize);
});
// 销毁页面
onBeforeUnmount(() => {
window.removeEventListener("resize", onResize);
});

人物大全页面

  • 左上角的 logo 图片在 PC 端距页面左侧较远,手机端较近,可对比下方两个图片,实现代码如下:

左上角 logo边距(PC 端)

左上角 logo边距(手机端)

/src/layout/index.vue
...
<!-- 使用相对宽度 3.5vw 适配不同宽度页面 -->
<a-layout-header class="clear-fix" style="background-color: white; padding: 0 3.5vw">
...
  • 页面标题的边距、内容区的边距,同见上图,实现方式基本相同,略

  • 每行的卡片数量不同,在 PC 端有 6 个,而手机端只有 2 个(跟设备无关,跟页面尺寸有关),同见上图,实现代码如下:

/src/views/record/personage/index.vue
...
<!-- 使用 xs/sm/lg 适配不同宽度页面 -->
<a-col v-for="personage in personageList" :xs="12" :sm="8" :lg="4">
...
  • 卡片中人物头像尺寸不同,同见上图,实现代码如下:
/src/views/record/personage/index.vue
...
<!-- 综合页面宽度、卡片个数、最大宽度/高度,得出最终的宽度/高度 -->
<a-avatar :src="personage.recordPersonageEntity?.avatar" :style="{width: scrollWidth() * cardProportion() * 0.32 + 'px', height: scrollWidth() * cardProportion() * 0.32 + 'px', 'max-width': '66px', 'max-height': '66px'}"/>
...
  • 分页器样式不同,在 PC 端功能更丰富,可对比下方两个图片,实现代码如下:

分页器样式(PC 端)

分页器样式(手机端)

/src/views/record/personage/index.vue
...
<!-- 根据页面宽度选择使用不同样式的分页器 -->
<a-pagination v-if="scrollWidth() >= 800" :current="query.pageNum" :page-size="query.pageSize" showSizeChanger style="margin-top: 2vh; text-align: center;" :total="personageTotal" :show-total="(total, range) => `${range[0]}-${range[1]} of ${total} items`" @change="paginationChange"/>
<a-pagination v-else :current="query.pageNum" :page-size="query.pageSize" style="margin-top: 2vh; text-align: center;" :total="personageTotal" simple @change="paginationChange"/>
...

人物详情页面

人物详情页面的锚点菜单,在 PC 端存在但手机端不存在,可对比下方两个图片,实现代码如下:

人物详情页面(PC 端)

人物详情页面(手机端)

/src/views/record/personage/detail.vue
...
<!-- 根据页面宽度选择内容区所占列数 -->
<a-col :span="scrollWidth() >= 800 ? 20 : 24">
...
<!-- 根据页面宽度选择是否显示锚点菜单 -->
<a-col v-if="scrollWidth() >= 800" span="4" style="background-color: white;">
<a-anchor :affix="true" :offsetTop="30" style="margin-top: 8px; margin-left: 10px" :items="anchorItems"></a-anchor>
</a-col>
...

总结

目前我的网站的页面较少,而且页面较简单,所以做终端适配很容易,涉及到的技术也不多,但是其方法和核心思想是通用的,我的建议是:

  1. 尽量不使用 px 等固定单位,推荐使用百分比单位(%、vw、vh)与相对单位(em、rem)
  2. 一定不要追求 PC 端与手机端的功能一致,手机端界面尽量简洁、操作尽量方便
  3. 一定不要追求技术实现方案统一,也一定不要反复打补丁,要设计先行、随机应变

本篇文章到这里就结束了,欢迎关注后续更新。