你的H5页面在折叠屏上适配了吗?

当我还沉浸在UI样式改版后的喜悦中时,QA同学拿着他的华为Mate X5(折叠屏手机)来找我:"这个字体在展开状态下太大了,影响用户体验"。这才意识到我们的H5页面一直没有在折叠屏设备上进行过充分测试。

1.主流适配方案调研

1.1 PostCSS + Viewport Units (vw) 方案

技术原理:

通过postcss-px-to-vw插件将设计稿中的px单位转换为vw单位,基于视口宽度进行动态缩放;

实现示例:

java 复制代码
// postcss.config.js
module.exports = {
  plugins: {
    'postcss-px-to-vw': {
      viewportWidth: 375,    // 设计稿宽度
      viewportUnit: 'vw',    // 转换单位
      minPixelValue: 1,      // 最小转换值
      mediaQuery: false      // 不转换媒体查询中的px
    }
  }
}

折叠屏适配实现:

css 复制代码
/* 基础样式 */
.title {
  font-size: 8.5333vw; /* 32/375 * 100 */
}

/* 折叠屏展开状态限制 */
@media (min-width: 540px) and (max-aspect-ratio: 1/1) {
  .title {
    font-size: clamp(16px, 4vw, 24px); /* 动态限制字体大小 */
  }
}

方案优点:

  • 原生CSS实现:无需JavaScript依赖,兼容性最佳
  • 真正的响应式:基于视口宽度动态缩放,适配效果自然
  • 性能优异:无运行时计算开销,渲染性能好
  • 维护简单:样式代码直观,易于理解和维护

方案缺点:

  • 字体控制复杂:小屏幕下字体可能过小,大屏幕下可能过大,需要额外使用clamp等函数控制
  • 历史项目迁移:需要重写所有样式,迁移成本高

1.2 CSS媒体查询方案

技术原理:

通过CSS媒体查询针对不同屏幕尺寸应用差异化样式,是最传统的响应式布局方案。

实现示例:

css 复制代码
/* 基础布局 */
.product-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}

/* 折叠屏展开适配 */
@media (min-width: 540px) and (max-aspect-ratio: 1/1) {
  .product-list {
    grid-template-columns: repeat(4, 1fr);
    gap: 2vw;
  }
}

方案优点:

  • 原生CSS支持:无需任何JavaScript,兼容性最好
  • 实现简单直接:通过简单的媒体查询即可实现差异化布局
  • 性能最优:无任何运行时开销,渲染效率最高
  • 控制精准:可以针对特定屏幕尺寸做精确控制

方案缺点:

  • 代码冗余度高:需要编写大量媒体查询规则,代码维护成本高
  • 适配粒度粗:只能基于断点做整体布局调整,无法实现精细化的动态缩放
  • 维护困难:随着断点增多,CSS代码变得复杂难以维护
  • 响应式体验差:布局切换不够平滑,用户体验不如动态缩放方案

1.3 flexible.js + postcss-px-to-rem现有适配方案

技术原理:通过flexible.js + postcss-px-to-rem插件将设计稿中的px单位转换为rem单位,基于视口宽度进行动态计算;

方案优势

1、高效开发流程

  • 自动单位转换 :直接按设计稿写px ,通过postcss-px-to-rem自动转换为rem
css 复制代码
/* 输入(设计稿 750px) */
.box { width: 200px; } 
/* 输出(rootValue: 75) */
.box { width: 2.6667rem; } /* 200/75 ≈ 2.6667 */
  • 设计稿1:1映射 :UI设计稿统一宽度是750px ,那么在postcss-px-to-rem设置rootValue: 75即可直观转换。

2、动态响应能力

  • 实时调整布局 :flexible.js内部会监听resize 事件,浏览器窗口大小变化时会自动重新计算根字体大小
javascript 复制代码
// 计算根字体大小
function refreshRem(){
  var width = docEl.getBoundingClientRect().width;
  if (width / dpr > 540) {
    width = 540 * dpr;
  }
  var rem = width / 10;
  docEl.style.fontSize = rem + 'px';
  flexible.rem = win.rem = rem;
}

// 监听resize事件
win.addEventListener('resize', function() {
  clearTimeout(tid);
  tid = setTimeout(refreshRem, 300);
}, false);

3、灵活覆盖与调试

  • 可选择性禁用转换 :通过selectorBlackList排除指定元素。
javascript 复制代码
// postcss 配置
selectorBlackList: ['.no-rem'], // 不转换 .no-rem 类下的样式
  • 可手动覆盖REM值 :必要时可直接写rem单位,优先级会高于插件转换。

4、性能优化

  • 构建时完成单位转换:postcss-px-to-rem在代码编译阶段处理,最终生成的CSS文件中已经是rem单位,无运行时计算开销。
  • 相比纯JavaScript方案更高效 :比实时计算vw 或动态rem的方案(如某些CSS-in-JS库)性能更好。

1.4 各方案对比分析

对比维度 flexible.js+postcss-px-to-rem postcss-px-to-vw CSS媒体查询
性能表现 有JS运行时开销 无运行时开销✅ 无运行时开销 ✅
折叠屏适配 一处适配,无需大范围代码改动✅ 自动适配,但字体难控 需要手动适配
历史项目迁移 无需迁移,直接优化✅ 需要重写所有样式 需要大量重构
字体控制精度 精确控制,符合设计✅ 需要额外限制方案 精确控制

基于以上多维度综合考量,团队最终选择优化现有方案(flexible.js+postcss-px-to-rem)作为最优解;

2. 具体实施与优化

现有方案限制了最大计算宽度540*dpr,导致折叠屏展开时字体过大,信息展示效率低。

这是一个商品详情页,折叠屏展开后首屏区域只有三个商品照片、商品标题、商品价格这三部分信息,而且字体放大,用户浏览效率低!

所以要解决的第一个问题,就是在flexible.js现有的540宽度基础上新增折叠屏的判断逻辑,目标是希望折叠屏展开后 ,字体和折叠起来的屏幕字体保持一致 ,可视区域内能展示更多的内容

2.1 新增折叠屏展开状态判断,解决屏幕展示效率低的问题

那如何判断折叠屏是展开状态呢?经过查询市面上目前的折叠屏屏幕参数:

📱 横向展开式折叠屏屏幕参数(2024主流机型)

品牌/型号 显示宽度(px) 显示高度(px) 宽高比(宽/高)
三星 Galaxy Z Fold5 804 967 0.83
华为 Mate X3 740 832 0.89
OPPO Find N3 896 960 0.93
小米 Mix Fold 3 957 1080 0.89
荣耀 Magic V2 719 780 0.92
vivo X Fold2 958 1080 0.89

📌 从上述数据中得出一些折叠屏的关键特征:

  • 宽高比 :集中在0.8~1.0之间(接近方形)

所以,在现有flexible.js的refreshRem 函数内增加宽高比的判断逻辑,当宽高比在0.8~1.0之间,就认为是折叠屏展开状态。

目的是展开状态后,字体大小和折叠状态下保持一致(非放大),首先需要了解折叠状态时,手机的屏幕宽度数据。

2.3 限制展开状态下计算尺寸

📱 折叠屏展开/折叠状态屏幕宽度(CSS像素)

品牌/型号 展开状态宽度(px) 折叠状态宽度(px)
三星 Galaxy Z Fold5 804 316
华为 Mate X3 740 360
OPPO Find N3 896 430
小米 Mix Fold 3 957 417
荣耀 Magic V2 719 301
vivo X Fold2 958 441

从数据得出折叠态宽度范围:300-450px (CSS像素),为了兼顾这个范围内的屏幕都能有比较好的体验,且简化计算逻辑,所以取中间值370作为基准,避免为每个机型单独适配。

代码实现逻辑如下:

javascript 复制代码
// 计算根字体大小
function refreshRem(){
  var width = docEl.getBoundingClientRect().width;
  if (width / dpr > 540) {
    width = 540 * dpr;
  }
  
  // 折叠屏展开判断
  var screenWidth = win.screen.width;
  var screenHeight = win.screen.height;
  if (screenWidth / screenHeight >= 0.8 && screenWidth / screenHeight < 1) {
    width = 370;
  }
  var rem = width / 10;
  docEl.style.fontSize = rem + 'px';
  flexible.rem = win.rem = rem;
}

// 监听resize事件
win.addEventListener('resize', function() {
  clearTimeout(tid);
  tid = setTimeout(refreshRem, 300);
}, false);

增加完这个逻辑后,就能在展开后,可视区域内的字体不放大,用户可预览更多的内容。

效果:可视区域内可浏览模块更多。

由于一些历史代码开发规范问题,会有模块内容宽度固定写死的问题存在,针对这类问题,整理成以下几类解决方案

3. 其他场景优化方案

3.1 商品列表布局

商品列表可使用媒体查询实现展开后一行2列 布局,展示更多商品,提高商品曝光量(这些实现可和设计同学进行讨论,不同的场景可采用不同的布局方案)。

示例代码:

css 复制代码
/* 默认1列布局 */
.product-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}

/* 当宽度≥540px时切换为2列(覆盖折叠屏展开态) */
/* 早期折叠屏(如Galaxy Fold)展开后浏览器视口约为540px,所以采用540进行处理 */
@media (min-width: 540px) {
  .product-list {
    grid-template-columns: repeat(2, 1fr);
  }
}

3.2 图片内容高度处理

固定图片内容

可设置图片宽度、高度不固定,根据图片实际宽高比进行缩放:

css 复制代码
.product-card img {
  width: 100%;
  aspect-ratio: 1/1; /* 明确设置宽高比 */
  object-fit: cover;
}

非固定图片内容

如需求内需要针对不同的屏幕尺寸,展示不同的图片内容时,可使用以下两种方式进行实现:

方案1:HTML的<picture>标签

html 复制代码
<picture>
  <!-- 折叠屏展开时加载 wide.jpg -->
  <source media="(min-width: 540px) and (max-aspect-ratio: 1/1)" srcset="wide.jpg">
  <!-- 默认加载 square.jpg -->
  <img src="square.jpg" alt="商品" class="product-img">
</picture>

方案2:JS动态替换(根据屏幕状态)

javascript 复制代码
function updateProductImages() {
  const ratio = window.innerWidth / window.innerHeight;
  // 是否折叠屏展开判断
  const isFoldableExpanded = ratio >= 0.8 && ratio < 1;
  
  document.querySelectorAll('.product-img').forEach(img => {
    img.src = isFoldableExpanded ? 'wide.jpg' : 'square.jpg';
  });
}

window.addEventListener('resize', updateProductImages);

个人更推荐优先使用CSS控制(不换图)。可减少HTTP请求,降低代码复杂度和特殊处理。

多行多列布局

可使用与商品列表处理的方式一样,一行2列变一行4列。

5. 总结与展望

5.1 技术方案总结

核心解决思路 通过分析现有flexible.js+postcss-px-to-rem方案的优势,我们采用了渐进式优化 而非推倒重来的策略。在保持团队开发习惯和项目稳定性的前提下,通过增加折叠屏检测逻辑,实现了对新型设备的适配。

关键技术要点

  1. 折叠屏状态检测:基于宽高比(0.8~1.0)判断展开状态
  2. 动态字体控制:展开状态下限制计算宽度为370px,保持字体一致性
  3. 响应式布局优化:通过媒体查询实现多列布局,提升信息密度
  4. 图片内容适配:支持不同屏幕状态下的图片展示策略

适配效果数据

  • 信息展示效率:折叠屏展开后可视内容增加约40%
  • 用户体验提升:字体大小保持一致,避免阅读疲劳
  • 开发维护成本:在现有方案基础上优化,迁移成本几乎为零

5.2 结语

H5页面在折叠屏上的适配需要综合考虑现有技术方案、业务需求和团队基建。通过优化现有的flexible.js+postcss-px-to-rem方案,成功兼容了折叠屏,提升了用户体验。

同时,我们也看到了其他方案的优势,为未来的技术优化和基建升级提供了方向。

转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。 关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~

相关推荐
崔庆才丨静觅11 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606112 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了12 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅12 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅12 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅13 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment13 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅13 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊13 小时前
jwt介绍
前端
爱敲代码的小鱼13 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax