大屏可视化适配方案
核心思路
基于设计稿 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.83vwvh(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. 关键点
- 使用 JS 计算字体大小,返回实际像素值
- 在 resize 事件中重新计算并更新字体
- 所有字体相关配置都使用 responsive 对象
- 调用 chart.resize() 重绘图表
Last updated on