Skip to content

rem 移动端搭建(react 项目)

背景

项目想支持 PC、移动端

方案

  • 根据不同端开发不同页面(成本最高)
  • 根据不同端加载不同 css 文件 (可取)
  • 根据响应式,允许不同样式规则(媒体查询 常见)
  • style 预处理器来做

考虑问题:

  • 视窗 原数据 <meta name="viewport" content="width=device-width,inital-scale=1.0"> </meta>
  • 媒体查询
css
@media (min-width: 300px) and (max-width: 750px) {
  body {
    font-size: 16px;
  }
}
  • 弹性布局flex

  • 响应式单位

    • rem(相对于html的font-size)
    • em

创建 Vite+React 项目

npm create vite@latest react-h5-rem -- --template react cd react-h5-rem

安装依赖

npm install

安装适配相关依赖

npm install postcss-pxtorem autoprefixer --save-dev

方案说明

适配原理 以 750px 设计稿为基准,1rem = 75px(设计稿宽度的 1/10) 通过 postcss-pxtorem 自动将 CSS 中的 px 转换为 rem 单位 动态计算根元素 fontSize,实现不同设备上的等比例缩放

js
//src/utils/rem 进行计算
// 动态设置根元素fontSize
function setRemUnit() {
  // 设计稿宽度为375px
  const designWidth = 375;
  // 实际设备宽度
  const deviceWidth = document.documentElement.clientWidth || window.innerWidth;

  // 限制最大宽度,避免在平板等大屏设备上字体过大
  const maxWidth = 750;
  const width = Math.min(deviceWidth, maxWidth);

  // 计算根元素fontSize,设计稿1px = 0.01rem
  // 例如:设计稿375px宽度对应根元素fontSize为37.5px,1rem = 37.5px
  document.documentElement.style.fontSize = (width / designWidth) * 37.5 + "px";
}

// 初始化
setRemUnit();

// 监听窗口大小变化
window.addEventListener("resize", setRemUnit);

// 监听屏幕旋转事件
window.addEventListener("orientationchange", setRemUnit);
js
// 在main.tsx引入
import "./utils/rem.js";
js
//post.config.js
import autoprefixer from "autoprefixer";
import pxtorem from "postcss-pxtorem";
export default {
  plugins: [
    autoprefixer(),
    pxtorem({
      rootValue: 37.5,
      propList: ["*"],
      unitPrecision: 5,
      selectorBlackList: ["ignore-rem"],
      replace: true,
      mediaQuery: false,
      minPixelValue: 1,
      exclude: /node_modules/i,
    }),
  ],
};

固定内容区域是 750px,且居中显示。 >750px 的设备则居中显示。 <750px 的缩放显示,最小的显示尺度在 375px。

css
#root {
  min-width: 375px;
  max-width: 750px;
  margin: 0 auto;
  padding: 16px;
  text-align: center;
  background-color: #ccc;
  overflow: auto;
  height: 100%;
}

数据大屏适配方案 (vw vh、rem、scale)

适配方案分析

方案实现方式优点缺点
vw vh1.按照设计稿的尺寸,将 px 按比例计算转为 vw 和 vh1.可以动态计算图表的宽高,字体等,灵活性较高 2.当屏幕比例跟 ui 稿不一致时,不会出现两边留白情况1.每个图表都需要单独做字体、间距、位移的适配,比较麻烦
scale1.通过 scale 属性,根据屏幕大小,对图表进行整体的等比缩放1.代码量少,适配简单 2.一次处理后不需要在各个图表中再去单独适配1.因为是根据 ui 稿等比缩放,当大屏跟 ui 稿的比例不一样时,会出现周边留白情况 2.当缩放比例过大时候,字体会有一点点模糊,就一点点 3.当缩放比例过大时候,事件热区会偏移。
rem + vw vh1.获得 rem 的基准值 2.动态的计算 html 根元素的 font-size 3.图表中通过 vw vh 动态计算字体、间距、位移等1.布局的自适应代码量少,适配简单1.因为是根据 ui 稿等比缩放,当大屏跟 ui 稿的比例不一样时,会出现周边留白情况 2.图表需要单个做字体、间距、位移的适配

如果想简单,客户能同意留白,选用 scale 即可 如果需要兼容不同比例的大屏,并且想在不同比例中都有比较好的效果,图表占满屏幕,类似于移动端的响应式,可以采用 vw vh 的方案 至于 rem,个人觉得就是 scale 和 vw vh 的综合,最终的效果跟 scale 差不多

方案一

实现思路

js
假设设计稿是 1920 * 1080
1920 -> 100vw
1080 -> 100vh

如果有个div是 300*300
width = (300/1920)*100vw
height = (300/1080)*100vh

css 方案 sass

css
/* utils.scss */
// 使用 scss 的 math 函数,https://sass-lang.com/documentation/breaking-changes/slash-div
@use "sass:math";

// 默认设计稿的宽度
$designWidth: 1920;
// 默认设计稿的高度
$designHeight: 1080;

@function vw($px) {
  @return math.div($px, $designWidth) * 100vw;
}

// px 转为 vh 的函数
@function vh($px) {
  @return math.div($px, $designHeight) * 100vh;
}
css
使用 .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;
}

图标字体、间距、位移等尺寸自适应

js
function fitChatSize(size,defaultWidth = 1920) {
    const clientWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
    if(!clientWidth) reutrn size;
    let scale = (clientWidth/defaultWidth)/size;
    return Number((size*scale).toFixed(3));
}

使用

js
// echart等配置
const option = {
  backgroundColor: "transparent",
  tooltip: {
    trigger: "item",
    formatter: "{a} <br/>{b} : {c}%",
  },
  grid: {
    left: fitChartSize(10),
    right: fitChartSize(20),
    top: fitChartSize(20),
    bottom: fitChartSize(10),
    containLabel: true,
  },
  // ....
};

方案二

实现思路: 当屏幕宽高比 < 设计稿宽高比,我们需要缩放的比例是屏幕宽度 / 设计稿宽度 。上下留白 当屏幕宽高比 > 设计稿宽高比,我们需要缩放的比例是屏幕高度 / 设计稿高度。 左右留白。

js
handleScreenAuto();
//初始化实现
window.onresize = () => handleScreenAuto();

function handleScreenAuto() {
  const designDraftWidth = 1920; //设计稿的宽度
  const designDraftHeight = 960; //设计稿的高度
  // 根据屏幕的变化适配的比例
  const scale =
    document.documentElement.clientWidth /
      document.documentElement.clientHeight <
    designDraftWidth / designDraftHeight
      ? document.documentElement.clientWidth / designDraftWidth
      : document.documentElement.clientHeight / designDraftHeight;
  // 缩放比例
  document.querySelector(
    "#screen"
  ).style.transform = `scale(${scale}) translate(-50%, -50%)`;
}

css 部分

css
.screen-root {
  height: 100%;
  width: 100%;
  #screen {
    display: inline-block;
    width: 1920px; //设计稿的宽度
    height: 960px; //设计稿的高度
    transform-origin: 0 0; //设置动画的基点
    translate(-50%, -50%);
    position: absolute;
    left: 50%; // 距离父亲50%
    top: 50%; 距离父亲50%
  }
}

方案三

实现思路:

  1. 计算 rem 基准值 1920/10 = 192;
  2. 所有元素长宽位置,字体全部转化为 rem
  3. 网页加载后,js 计算浏览器高宽度,并设置 html 的 fontsize

初始计算 fontSize

js
(function init(screenRadioByDesign = 16 / 9) {
  let doEle = document.documentElement;
  function setHtmlFontSize() {
    const clientRadio = doEle.wdith / doEle.height;
    const scale =
      clientRadio > screenRadioByDesign ? clientRadio / screenRadioByDesign : 1;
    const fontSize = (scale * doEle.wdith) / 10;
    doEle.style.fontSize = fontSize.toFixed(3) + "px";
  }
  setHtmlFontSize;
  window.addEventlistenr("resize", setHtmlFontSize);
})();

图标的字体、间距、位移用方案一 vw、vh 的实现方式。

Released under the MIT License.