【不到20行代码】🔥这才是正确的可视化大屏适配方案!(附Demo和源码)

4,918 阅读7分钟

大家好,我是前端架构师,关注微信公众号【程序员大卫】:

  • 回复 [面试] :免费领取“前端面试大全2025(Vue,React等)”
  • 回复 [架构师] :免费领取“前端精品架构师资料”
  • 回复 [书] :免费领取“前端精品电子书”
  • 回复 [软件] :免费领取“Window和Mac精品安装软件”

当牛马的一天

产品:小明(前端),老板想做一个大屏展示公司业务,请尽快安排。

设计:小明,你要什么尺寸,请尽快告诉我。

小明:啊,我没做过呀,给我点时间调研下。

小明压力好大,先去蹲了个厕所,用手机打开谷歌、掘金,搜索大屏适配方案,什么remvw/vhscale等,我到底选哪种比较好呢?

-------------马上到了午饭的时间------------

产品:怎么样了,小明?

小明:是这样的,老板想展示的大屏是只放在我们公司呢,还是去客户现场也会展示大屏?

产品:老板的心思你不要猜,猜来猜去也猜不着......

小明(心想):what the f**k....

----------终于,小明看到了这篇文章----------

小明自信的娓娓道来:

  • 市场上常见的大屏的分辨率是 >=16:9,平时我们说的比较多的 1080p(1920 × 1080)2K(2560 × 1440)4K(3840 × 2160)的宽高比都是 16:9
  • (对设计说)你先可以设计一个 2K 分辨率的吧。
  • (对产品说)不管我们的大屏在哪种分辨率下显示,我都有信心展示好,记得在老板面前美言我几句,给我加点工资......啊,说错了❌,不要加工资了,如果公司裁员了,先不要裁我。

背景

可视化大屏是每个前端工程师职业生涯中可能会遇到的一个挑战。初次接触时,我也曾感到迷茫,不知从何入手。在网上搜索了一番,发现有多种适配方案,例如使用 remvw/vh 以及 scale 等。然而,其中大部分方法无法做到真正的全屏适配,难以满足业务需求。

实际上,当你掌握了大屏适配的核心原理后,就会发现它并不复杂。

调研大屏的尺寸

不同公司的业务场景对大屏的尺寸要求不同,但主流行业,如互联网、证券金融等,通常采用的宽高比>=16:9

注:你可以根据自身业务特点,对接下来的代码方案进行适当调整。

宽高比特点典型分辨率应用场景
16:9主流比例,适配高清视频和设计工具1920×1080(1080p)、3840×2160(4K)会议展示、数据监控中心
16:10纵向空间更大,适合多行数据展示1920×1200、2560×1600商业汇报屏、高端会议室
4:3传统比例,兼容老旧系统1024×768、1600×1200工业控制室、老旧监控系统
21:9超宽视野,适合横向延展内容2560×1080、3440×1440交通调度中心、金融实时看板
32:9极致宽度,可分割为多个虚拟屏幕3840×1080、5120×1440多任务监控(数据、视频、日志并行展示)
拼接屏比例灵活,支持多屏组合2×2(16:9)、3×3(方形)指挥中心、展览馆、大型数据看板
定制比例根据需求设计异形或特殊比例3:1、1:1 等超长信息流(如股票行情)、艺术装置交互屏

实现方案

适配思路

我们的方案基于 16:9 进行设计。

1. 当宽高比等于 16:9

此时,页面能够完美铺满屏幕。

2. 当宽高比小于 16:9

效果如下,可能有人会疑惑:为什么页面没有完全铺满?

正如前面提到的,国内市场上主流的大屏宽高比通常大于 16:9。因此,当屏幕宽高比小于 16:9 时,页面底部会留出一些空白区域,但这并不影响整体的展示效果。

如果在这种情况下强行让页面高度铺满,不仅会影响美观,还会导致所有与高度相关的计算都需要转换为百分比,增加开发难度,降低效率。

3. 当宽高比大于 16:9

此时,头部适配较为简单,而主体部分需要与产品和设计师讨论,决定哪些内容需要横向扩展,以达到最佳展示效果。

代码实现

1. 适配核心代码 - flexible.ts

我们需要判断当前屏幕的宽高比是否大于 16:9:

  • 如果 大于 16:9,则计算 scaleRatio = 页面高度 / 1080
  • 如果 小于 16:9,则计算 scaleRatio = 页面宽度 / 1920

计算 scaleRatio 的核心代码如下:

注: 1920 是设计稿的宽度,1080是 设计稿的高度。

const enum BaseSize {
  WIDTH = 1920, // 设计稿的宽度
  HEIGHT = 1080, // 设计稿的高度
  FONT_SIZE = 16,
}

const calculateScaleRatio = () => {
  const { clientWidth, clientHeight } = document.documentElement;
  
  return clientWidth / clientHeight > BaseSize.WIDTH / BaseSize.HEIGHT
    ? clientHeight / BaseSize.HEIGHT
    : clientWidth / BaseSize.WIDTH;
};

const scaleRatio = calculateScaleRatio();

接着,我们将 scaleRatio * 16 设置到 htmlfont-size,以适配 rem 计算。

“为什么要乘以 16?因为 HTML 的默认 font-size 是 16px,而 Tailwind CSS 也是基于 16px 计算 rem 的。”

document.documentElement.style.fontSize = `${scaleRatio * BaseSize.FONT_SIZE}px`;

另外,我们还可以在 html 上存储 data-scaleRatio,方便 Echarts 组件获取这个值来做适配。

document.documentElement.dataset.scaleRatio = scaleRatio.toString();

🎉🎉🎉 最终完整核心代码如下,不足 20 行(除去空行)!

enum BaseSize {
  WIDTH = 1920, // 设计稿的宽度
  HEIGHT = 1080, // 设计稿的高度
  FONT_SIZE = 16,
}

const calculateScaleRatio = () => {
  const { clientWidth, clientHeight } = document.documentElement;
  return clientWidth / clientHeight > BaseSize.WIDTH / BaseSize.HEIGHT
    ? clientHeight / BaseSize.HEIGHT
    : clientWidth / BaseSize.WIDTH;
};

function updateRootFontSize() {
  const scaleRatio = calculateScaleRatio();
  document.documentElement.dataset.scaleRatio = scaleRatio.toString();
  document.documentElement.style.fontSize = `${
    scaleRatio * BaseSize.FONT_SIZE
  }px`;
}

updateRootFontSize();

window.addEventListener("resize", updateRootFontSize);

3. 安装插件进行单位转换

⚠️ 注:是否安装以下插件可根据具体业务需求决定,并非必须。

  • postcss-pxtorem 插件的主要作用是自动将 px 单位转换为 rem,例如,当你写 width: 100px; 时,它会自动转换为相应的 rem 值。
  • tailwindcss 是一款原子化 CSS 框架,能够提高开发效率,方便快速构建样式。

1. 安装 tailwindcsspostcss-pxtorem

pnpm i tailwindcss@^3.4.17 postcss-pxtorem postcss -D

2. 配置 tailwind.config.tspostcss.config.js

在项目根目录创建 tailwind.config.ts,并写入以下代码:

import type { Config } from "tailwindcss";

export default {
  content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
} satisfies Config;

在项目根目录创建 postcss.config.ts,并写入以下代码:

export default {
  plugins: {
    "postcss-import": {},
    "tailwindcss/nesting": {},
    tailwindcss: {},
    "postcss-pxtorem": {
      rootValue: 16, // 根元素的字体大小,通常设置为 16px
      propList: ["*"], // 需要转换的属性,* 表示全部属性都转换
      minPixelValue: 2, // 需要转换的最小 px 值,忽略 1px 转换成 rem
    },
    ...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}),
  },
};

3. 在 main.ts 中引入 flexible.tstailwind.css

import "./styles/tailwind.css"; // 引入 TailwindCSS
import "./utils/flexible"; // 引入适配脚本

// 其它代码

4. 编写 HTML 页面结构

从下面的 html 可以看到,我们写死了 header 的高度为 400px,主体高度为 680px,它们相加刚好等于 1080px

<template>
  <div class="bg-yellow-400 h-[400px]"></div>
  <div class="grid grid-cols-[1fr_3fr] h-[680px]">
    <div class="bg-blue-400"></div>
    <div class="bg-red-400"></div>
  </div>
</template>

另外在项目开发过程中,可以结合 flexgridvw/vh 等来进行布局,另外可再封装一个可适配的 ECharts 图表组件。

4. Echarts 组件如何获取 scaleRatio

我们可以通过以下两种方式获取 document.documentElement 根元素上的 data-scale-ratio 属性值:

方法一:监听 windowresize 事件

每当窗口大小发生变化时,我们就从 document.documentElement 上读取最新的 scaleRatio

import { ref } from "vue";
import { useEventListener } from "@vueuse/core";

export function useScaleRatio() {
  const scaleRatio = ref(1);

  useEventListener(window, "resize", () => {
    scaleRatio.value = parseFloat(
      document.documentElement.dataset.scaleRatio ?? "1",
    );
  });

  return scaleRatio;
}

方法二:使用 MutationObserver 监听属性变化

如果 scaleRatio 是通过修改 data-scale-ratio 属性动态设置的,可以使用 MutationObserver 实时监听该属性变化:

import { ref } from "vue";
import { useMutationObserver } from "@vueuse/core";

export function useScaleRatio() {
  const getScaleRatio = () =>
    parseFloat(document.documentElement.dataset.scaleRatio ?? "1");
  const scaleRatio = ref(getScaleRatio());

  useMutationObserver(
    document.documentElement,
    (mutations) => {
      if (mutations[0]?.attributeName === "data-scale-ratio") {
        scaleRatio.value = getScaleRatio();
      }
    },
    {
      attributes: true,
    },
  );

  return scaleRatio;
}

总结

  1. 了解屏幕比例:大多数可视化大屏的宽高比大于 16:9,不同应用场景有不同的适配需求。
  2. 选择合适的适配方案:使用 scaleRatio 计算缩放比例,并根据屏幕尺寸调整 rem
  3. 兼容不同屏幕:当宽高比不同于 16:9 时,需要合理处理页面布局,避免错位影响展示效果。
  4. 灵活扩展:可以结合 data-scaleRatio 适配 Echarts 图表,提升数据可视化的体验。

通过上述方案,我们可以确保可视化大屏在不同设备上的适配效果,实现高质量的数据展示。

Demo

codesandbox.io/p/devbox/ha…

源码地址

github.com/zm8/wechat-…