Skip to Content
路漫漫其修远兮,吾将上下而求索

大屏可视化适配方案

核心思路

基于设计稿 1920x1080,使用 vw/vh 实现响应式布局,使用 flexbox 避免高度累加问题,使用 clamp() 限制字体大小范围。

技术栈

  • Sass 函数封装 vw/vh
  • Flexbox 布局
  • CSS clamp() 函数

一、安装依赖

npm install -D sass

二、创建工具函数

创建 src/styles/utils.scss

// 设计稿尺寸 $design-width: 1920; $design-height: 1080; // px 转 vw @function vw($px) { @return ($px / $design-width * 100vw); } // px 转 vh @function vh($px) { @return ($px / $design-height * 100vh); } // 字体适配(带最小/最大限制) @function font($px, $min: null, $max: null) { $vw-value: calc($px / $design-width * 100vw); @if $min and $max { @return clamp(#{$min}px, $vw-value, #{$max}px); } @else if $min { @return max(#{$min}px, $vw-value); } @else if $max { @return min($vw-value, #{$max}px); } @else { @return $vw-value; } }

三、使用方式

1. 布局尺寸

关键原则:

  • 宽度、水平间距使用 vw() 函数
  • 高度使用 flexbox 自适应,不要给多个容器设置固定 vh 高度
  • 垂直间距可以使用 vh() 函数
@use '@/styles/utils.scss' as *; .container { width: 100vw; height: 100vh; display: flex; flex-direction: column; } .header { width: 100%; height: vh(100); // 可以用 vh() 函数 flex-shrink: 0; // 固定高度不收缩 } .content { flex: 1; // 自动填充剩余空间 width: vw(1880); gap: vw(20); // 水平间距用 vw min-height: 0; // 防止 grid 撑开 } .box { width: vw(400); padding: vw(20); border-radius: vw(8); gap: vh(20); // 垂直间距可以用 vh() 函数 }

2. 字体使用 font() 函数

.title { font-size: font(48, 32, 64); // 设计稿 48px,最小 32px,最大 64px } .text { font-size: font(16, 12, 20); // 设计稿 16px,最小 12px,最大 20px }

四、核心原理

vw/vh 计算公式

vw = (设计稿px / 设计稿宽度) * 100vw vh = (设计稿px / 设计稿高度) * 100vh

例如:

  • vw(400) = (400 / 1920) * 100vw = 20.83vw
  • vh(100) = (100 / 1080) * 100vh = 9.259vh

clamp() 函数

font-size: clamp(最小值, 理想值, 最大值);

浏览器会自动选择:

  • 如果理想值 < 最小值,使用最小值
  • 如果理想值 > 最大值,使用最大值
  • 否则使用理想值

五、ECharts 字体适配

1. 创建工具函数

创建 src/utils/responsive.ts

const DESIGN_WIDTH = 1920 const DESIGN_HEIGHT = 1080 export function font(px: number, min?: number, max?: number): number { const vwValue = (px / DESIGN_WIDTH) * window.innerWidth if (min && max) { return Math.max(min, Math.min(vwValue, max)) } else if (min) { return Math.max(min, vwValue) } else if (max) { return Math.min(vwValue, max) } return vwValue } export function getEchartsResponsive() { return { titleFontSize: font(24, 18, 32), legendFontSize: font(14, 12, 18), axisFontSize: font(12, 10, 16), tooltipFontSize: font(14, 12, 18), labelFontSize: font(12, 10, 16), } }

2. 在 ECharts 中使用

import * as echarts from 'echarts' import { getEchartsResponsive } from '@/utils/responsive' const chartRef = useRef<HTMLDivElement>(null) const chartInstance = useRef<echarts.ECharts>() useEffect(() => { if (!chartRef.current) return chartInstance.current = echarts.init(chartRef.current) const responsive = getEchartsResponsive() const option: echarts.EChartsOption = { title: { text: '示例图表', textStyle: { fontSize: responsive.titleFontSize, color: '#fff' } }, legend: { textStyle: { fontSize: responsive.legendFontSize, color: '#fff' } }, tooltip: { textStyle: { fontSize: responsive.tooltipFontSize } }, xAxis: { axisLabel: { fontSize: responsive.axisFontSize, color: '#fff' } }, yAxis: { axisLabel: { fontSize: responsive.axisFontSize, color: '#fff' } }, series: [ { label: { fontSize: responsive.labelFontSize } } ] } chartInstance.current.setOption(option) // 监听窗口变化 const handleResize = () => { chartInstance.current?.resize() const newResponsive = getEchartsResponsive() chartInstance.current?.setOption({ title: { textStyle: { fontSize: newResponsive.titleFontSize } }, legend: { textStyle: { fontSize: newResponsive.legendFontSize } }, xAxis: { axisLabel: { fontSize: newResponsive.axisFontSize } }, yAxis: { axisLabel: { fontSize: newResponsive.axisFontSize } }, series: [{ label: { fontSize: newResponsive.labelFontSize } }] }) } window.addEventListener('resize', handleResize) return () => { window.removeEventListener('resize', handleResize) chartInstance.current?.dispose() } }, []) return <div ref={chartRef} style={{ width: '100%', height: '100%' }} />

3. 关键点

  1. 使用 JS 计算字体大小,返回实际像素值
  2. 在 resize 事件中重新计算并更新字体
  3. 所有字体相关配置都使用 responsive 对象
  4. 调用 chart.resize() 重绘图表

Last updated on