超级响应式布局
响应式布局的概念
响应式布局是指网页在不同的设备和屏幕尺寸下,能够自动调整布局和样式,以适应不同的用户需求。
传统前端响应式的问题
在传统的前端项目中,通过屏幕宽度的判断,在CSS中通过媒体查询,来实现不同的布局和样式。但是这种方式存在以下几个问题:
- 代码复杂,维护成本高。
- 无法实现自适应布局。
- 无法实现自适应字体大小。
超级响应式的解决方案
在知鸢宫的响应式布局设计中,体验最好的做法是将响应式布局与自适应布局二者结合:结合 TailWindCSS 用横竖屏媒体查询处理布局切换,以及一些必要断点处的字号数值的变化。同一屏幕方向、同一断点区间内,单位使用 rem 和百分比。除此之外,使用JavaScript,给根节点在相同断点区间、相同屏幕方向内设置线性根字号变化;在不同的断点区间、不同的屏幕方向内,设置字号跳变。
超级响应式的优点
超级响应式的优点主要有以下几个方面: 这套做法可以最灵活的满足包括手机横屏、pc竖屏等小众设计要求的方案,达到任何奇怪的屏幕尺寸都完美显示。
- 代码简洁,维护成本低。
- 可以实现自适应布局。
- 可以实现自适应字体大小。
超级响应式的断点设计
超级响应式布局系统,共可以实现以下12种屏幕尺寸的分别精细化设计:
屏幕大小(可自定义响应点) | 屏幕方向 | 说明 | 默认设计稿尺寸(可自定义) | TailWind CSS 断点前缀 |
---|---|---|---|---|
<600px | portrait | 手机竖屏 | 375px | portrait: |
<600px | landscape | 手机横屏 | 667px | landscape: |
600px - 900px | portrait | 小平板竖屏 | 576px | portrait:xs: |
600px - 900px | landscape | 小平板横屏 | 1024px | landscape:xs: |
900px - 1200px | portrait | 大平板竖屏 | 720px | portrait:sm: |
900px - 1200px | landscape | 大平板横屏 | 1280px | landscape:sm: |
1200px - 1500px | portrait | 小笔记本竖屏 | 768px | portrait:md: |
1200px - 1500px | landscape | 小笔记本横屏 | 1366px | landscape:md: |
1500px - 1800px | portrait | 大笔记本竖屏 | 900px | portrait:lg: |
1500px - 1800px | landscape | 大笔记本横屏 | 1600px | landscape:lg: |
>1800px | portrait | 显示器竖屏 | 1080px | portrait:xl: |
>1800px | landscape | 显示器横屏 | 1920px | landscape:xl: |
JavaScipt自适应字号原理
对于两个断点之间的字号的线性变化,我们可以通过以下公式来计算:
\text{rem} = \text{fontSize} \times \left( \frac{\text{docEl.clientWidth}}{\text{designSize}} \right)
此公式的主要目标是根据当前浏览器窗口的宽度(docEl.clientWidth
),结合设计稿的尺寸(designSize
)和基础字体大小(fontSize
),动态地计算出 HTML 根元素(<html>
)的字体大小(rem
),从而实现页面的响应式布局。
公式各部分含义
fontSize
:这是基础字体大小,它是一个固定的值,存储在window.ZyFontSize
中。在响应式布局中,它作为计算的基础,所有后续的字体大小调整都是基于这个基础值进行的。docEl.clientWidth
:表示当前 HTML 文档根元素(<html>
)的可视宽度,即浏览器窗口的宽度。这个值会随着用户调整浏览器窗口大小而动态变化。designSize
:它是根据当前窗口宽度和屏幕方向从window.ZyDesignSize
中选取的一个合适的设计稿尺寸。不同的窗口宽度范围和屏幕方向对应不同的设计稿尺寸,例如手机竖屏、横屏,平板竖屏、横屏等。
公式计算过程
- 计算比例:首先计算
docEl.clientWidth
与designSize
的比值,即docEl.clientWidth / designSize
。这个比值表示当前窗口宽度相对于设计稿尺寸的比例。例如,如果设计稿宽度为 375px,而当前窗口宽度为 750px,那么这个比值就是 2,表示当前窗口宽度是设计稿宽度的 2 倍。 - 计算
rem
值:将基础字体大小fontSize
乘以这个比例,得到最终的rem
值。这样,rem
值就会随着窗口宽度的变化而动态调整。例如,如果基础字体大小为 16px,当前窗口宽度是设计稿宽度的 2 倍,那么计算得到的rem
值就是16 × 2 = 32
px。 - 设置根元素字体大小:最后将计算得到的
rem
值加上px
单位,并设置为 HTML 根元素的字体大小。由于页面中的其他元素可以使用rem
作为单位来设置字体大小和其他尺寸,因此当根元素的字体大小改变时,整个页面的布局会自动进行响应式调整。
通过这个公式,页面可以根据不同的屏幕尺寸和方向,动态地调整根元素的字体大小,从而实现响应式布局,确保页面在各种设备上都能有良好的显示效果。
技术细节
在项目开发时,可以结合 Tailwind CSS 的responsive modifiers
响应修饰符特性,结合rem
单位,实现页面的响应式布局。例如一个完整的文章列表的 Grid 布局,具体的 Tailwind ClassName 实现方式如下:
.row-article {
@apply grid grid-cols-12 portrait:gap-0 portrait:xs:gap-4 gap-4 xs:gap-4 sm:gap-4 md:gap-5 lg:gap-6;
}
.clo-article-card {
@apply portrait:col-span-12 landscape:col-span-12
portrait:xs:col-span-12 landscape:xs:col-span-6
portrait:sm:col-span-6 landscape:sm:col-span-6
portrait:md:col-span-6 landscape:md:col-span-6
portrait:lg:col-span-6 landscape:lg:col-span-6
portrait:xl:col-span-6 landscape:xl:col-span-6;
}
像这样结合使用portrait:
方向断点前缀和xs:
宽度断点前缀,即可分别设置12种屏的样式。
而对于自适应字号,知鸢宫的超级响应式组件ZySuperResponsive
,已经自动处理了通过 JavaScipt 实现动态 HTML 根元素字号大小的设计。只需要开发者编写CSS样式时使用rem
单位,或者直接使用 TailWind CSS 。因为它默认就是采用的rem
单位。
ZySuperResponsive
组件中,自适应字号的核心代码:
const isVertical = () => {
const mql = window.matchMedia("(orientation: portrait)");
return mql.matches;
};
const fontSizeInit = () => {
const docEl = document.documentElement;
const fontSize = window.ZyFontSize;
const breakPoints = window.ZyBreakPoints;
const designSize = window.ZyDesignSize;
if (docEl) {
const width = docEl.clientWidth;
const isVert = isVertical();
// 定义尺寸映射对象
const sizeMap = {
xs: isVert ? designSize.min_xs_v : designSize.min_xs_h,
sm: isVert ? designSize.xs_sm_v : designSize.xs_sm_h,
md: isVert ? designSize.sm_md_v : designSize.sm_md_h,
lg: isVert ? designSize.md_lg_v : designSize.md_lg_h,
xl: isVert ? designSize.lg_xl_v : designSize.lg_xl_h,
max: isVert ? designSize.xl_max_v : designSize.xl_max_h,
};
let rem;
if (width <= breakPoints.xs) {
rem = fontSize * (width / sizeMap.xs);
} else if (width <= breakPoints.sm) {
rem = fontSize * (width / sizeMap.sm);
} else if (width <= breakPoints.md) {
rem = fontSize * (width / sizeMap.md);
} else if (width <= breakPoints.lg) {
rem = fontSize * (width / sizeMap.lg);
} else if (width <= breakPoints.xl) {
rem = fontSize * (width / sizeMap.xl);
} else {
rem = fontSize * (width / sizeMap.max);
}
docEl.style.fontSize = rem + "px";
}
};
兼容 SSR 服务端渲染的实现方法
该核心方法中使用了document
对象,在 SSR 服务端渲染时不存在document
对象,因此需要通过一些方法使其只在客户端运行。
为了在页面元素及其他页面样式加载之前,就提前自动初始化该样式,并且兼容 SSR 服务端渲染,ZySuperResponsive
组件在服务端渲染时,通过 NUXT3 的useHead()
方法,在 html 头部插入标签,包括window.ZyFontSize
和window.ZyDesignSize
等全局变量,以及一些需要客户端优先加载的样式类脚本,来动态设置 HTML 根元素的字体大小。具体的实现方法如下:
useHead({
script: [
{
innerHTML: `
window.ZyFontSize = ${props.baseFontSize};
window.ZyBreakPoints = ${JSON.stringify(props.breakPoints)};
window.ZyDesignSize = ${JSON.stringify(props.designSize)}
`,
},
{
innerHTML: `${initZySuperResponsive.toString()};initZySuperResponsive();`,
},
],
})
该标签在浏览器环境会被同步加载,且在 Node.js 环境不会运行。