ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Vue大屏自适应实战:等比缩放、ECharts适配与排错

Vue大屏自适应实战:等比缩放、ECharts适配与排错 大屏项目接得多了你会发现一个规律需求方永远只会说一句“我这块屏是 1920 的你按这个做”然后交付那天你站在现场面对的是一台 3840×2160 的拼接屏或者一台 2560×1080 的带鱼屏旁边还站着一个问你“为什么两边有黑边”的领导。Vue 大屏自适应这件事说穿了就是把“设计稿的固定坐标系”和“物理屏幕的千奇百怪”这两件事撮合到一起撮合得好代码写完再也不用改撮合得不好你会在项目验收前一周反复调 margin。我自己做过的可视化大屏项目从森林防火指挥中心那种 4×4 拼接墙到工厂车间里挂在墙上的单块 55 寸看板再到给客户演示用的笔记本外接投影屏幕尺寸和比例基本没有重样过。踩过的坑包括但不限于打包上线后布局整体错位、ECharts 图表糊成一片、鼠标点击位置和视觉位置差半米、字体在 4K 屏上细得像蚂蚁腿。这篇文章就把这些年摸索出来的一套 Vue 大屏自适应方案完整拆开讲从方案选型的取舍逻辑到可直接复制运行的组合式函数再到上线后出问题的排查手册尽量做到你看完就能套到自己项目里。1. 大屏自适应到底难在哪先把问题的本质想清楚1.1 大屏项目和普通后台管理系统的根本差异普通后台管理系统用户是在自己电脑上用的浏览器窗口大小随时在变所以我们的思路是“流式布局 弹性容器”内容随窗口自适应滚动条该出就出。大屏完全不是这个逻辑它是一块固定物理尺寸的屏幕通常是 16:9展示距离三到五米观众站着看没有交互或者只有极少的点击交互。这个场景决定了三件事。第一大屏不允许出现滚动条。任何一个方向上的滚动都会让整块屏的构图崩掉所以必须一屏装下所有内容。第二大屏的字体和元素尺寸是“视觉尺寸”不是“可读尺寸”1920 设计稿上 14px 的字放到 4K 屏上如果按 1:1 渲染三米外根本看不见所以必须跟着屏幕等比放大。第三大屏的布局是“版式化”的设计师给你的是一张完整的 1920×1080 效果图每个模块的位置、大小、间距都是确定的你要做的是精确还原这张图而不是让它自由流动。把这三条想明白你就会发现传统的那套 Flex 弹性布局在大屏上其实是个负担。你越是用 flex: 1、百分比、min-width 这些弹性手段最终的视觉效果离设计稿就越远。大屏要的是“锁死比例”而弹性布局追求的是“随遇而安”方向本身就是反的。1.2 三种主流适配路线的取舍逻辑市面上做大屏适配跑不出三条路rem/vw 换算、transform 缩放、以及媒体查询断点。我逐个说说我的实际体验。rem/vw 方案的核心是找一个基准把设计稿上的 px 全部换算成 rem 或者 vw。比如 1920 设计稿1vw 19.2px那么设计稿上 100px 宽的盒子就写成 5.208vw。这个方案的好处是布局是“真弹性”的宽高比不同的屏幕上横向和纵向可以独立伸缩不会出现黑边。坏处也非常明显你得把所有 px 都换算一遍写 CSS 的时候脑子里一直在做除法更麻烦的是高度方向vw 只管宽度高度得靠 vh一旦屏幕比例变了元素会被拉扁或者抻长圆形变椭圆。transform 缩放方案是给整个内容容器设一个固定的设计稿尺寸然后用transform: scale()把它整体缩放到填满屏幕。这个方案最大的优点是写代码的时候完全不用换算设计稿上写多少 px 就写多少 px一行scale()搞定全部。缺点是在宽高比不一致的屏幕上要么留黑边等比缩放要么变形非等比缩放。媒体查询断点方案就是针对几种已知的目标分辨率各写一套样式1920 一套、2560 一套、3840 一套。这个方案听起来最土但在某些固定场景下反而最稳比如你的大屏就是固定那几块永远不变。缺点是维护成本高样式要写三份改一个需求要同步改三处。我的选择是以 transform 等比缩放为主干以 vw/vh 处理背景和外框为辅以媒体查询做兜底微调。三条路都不放弃但各司其职。后面我会详细讲这个混合方案怎么落地。注意不要迷信“一套方案打天下”。我在一个项目里硬上 rem 方案结果遇到一块 32:9 的超宽屏纵向被压得没法看最后返工改成缩放方案。方案选型一定要先问清楚这块屏是固定尺寸还是要在多种设备上展示2. 方案选型为什么我最终选了等比缩放打底2.1 把等比缩放的计算过程拆开看先明确几个变量。设计稿宽度DW设计稿高度DH比如 1920×1080。当前窗口可用宽度CW可用高度CH。缩放比例scale。等比缩放的算法很简单const scaleX CW / DW const scaleY CH / DH // 取小值保证内容完整装下代价是可能留边 const scale Math.min(scaleX, scaleY)如果取Math.max(scaleX, scaleY)内容会被裁掉一部分适合背景图这种“宁可裁掉也不能留白”的场景。如果直接scaleX和scaleY分别用就是拉伸变形适合设计稿本身就跟屏幕同比例的情况。算出 scale 之后缩放层的位置也要一起算否则内容会贴着左上角右边和下边空一大堆。居中的算法const offsetX (CW - DW * scale) / 2 const offsetY (CH - DH * scale) / 2然后应用样式.screen-wrapper { width: 1920px; height: 1080px; transform-origin: left top; /* transform 和 margin 由 JS 动态注入 */ }这里有个细节值得说一下我用transform: translate()配合scale()而不是用margin-left来居中。原因是translate不会触发重排只走合成层在缩放比例频繁变化的场景下性能更好。实际写法是el.style.transform translate(${offsetX}px, ${offsetY}px) scale(${scale})注意transform-origin必须是left top因为我们要自己算偏移量。如果用的是默认的center center那偏移量的计算公式就完全不同了很多人在这上面栽过跟头。2.2 设计稿基准尺寸到底该定多少这是个看似无关紧要、实际上影响全局的决定。我的经验是设计稿基准就按设计师给你的实际尺寸来不要自作聪明地改成 1920×1080。为什么这么说因为设计师给你的稿子如果是 3840×2160 的说明他已经在高分辨率下画好了所有元素的精确位置和字号。你如果强行把它按 1920×1080 来写就得把所有标注数值除以 2这个操作极其容易出错而且后面设计师改稿你还得跟着改。我曾经在一个项目里遇到过设计师给的是 3840 的稿子我图省事按 1920 写结果有个模块的间距应该是 24px我除错了写成 14px上线之后模块之间挤在一起排查了两个小时才发现是换算错误。从那以后我就定了个规矩设计稿多少基准就多少。当然如果设计稿确实是 1920×1080那就用 1920×1080。基准尺寸只影响DW和DH两个参数不影响方案的任何其他部分。2.3 宽高比不一致时留白区域该怎么办等比缩放最让人头疼的就是留白。比如设计稿 16:9实际屏幕 21:9左右各会空出一条。这条留白处理得不好整个大屏看起来就像是“没做完”。我的处理方式是把页面拆成三层。背景层绝对定位铺满整个视口用background-size: cover或者直接一张纯色/渐变完全不参与缩放。这样留白区域看起来也是“有内容”的。内容层也就是那个被 scale 的容器负责所有实际的数据展示模块等比缩放居中。装饰层一些边缘的光效、边框、扫描线动效同样铺满视口但透明度很低主要作用是让留白区域不那么突兀。背景层还有一个额外的坑如果背景是一张位图在 4K 屏上被cover拉伸会糊。这时候要么找设计师出一张 3840 宽的高清图要么用 CSS 渐变 SVG 图形代替位图后者在任意分辨率下都不会糊我现在基本都走这条路。实操心得给背景留白区域加一层很淡的径向渐变中心亮、边缘暗视觉上会把观众的注意力往中间的内容区拉留白就不那么明显了。这个技巧是跟做展陈的朋友学的成本几乎为零。3. 核心实现封装一个能复用的 Vue 自适应 Hook3.1 项目初始化和目录约定先用 Vue 3 Vite 起项目这套组合在大屏项目里体验最好打包快、热更新快改一行样式几乎秒级刷新。npm create vitelatest big-screen -- --template vue cd big-screen npm install npm install echarts vue-router pinia npm install -D sass目录我一般这么组织src/ adapt/ # 自适应相关核心目录 useScreenAdapter.js constants.js components/ # 通用大屏组件 PanelBox.vue # 带边框和标题的面板容器 ScrollList.vue # 滚动列表 views/ Dashboard.vue # 大屏主页面 charts/ # 图表封装 useChart.js assets/ styles/ global.scss把自适应逻辑单独放在adapt目录里是因为这部分代码几乎每个大屏项目都要原样搬过去抽出来之后复用成本极低。我在三个项目里用的都是同一份useScreenAdapter.js只改了两个参数。3.2 useScreenAdapter 组合式函数的完整实现这是整套方案的核心我把它写完整你可以直接拿去用。// src/adapt/useScreenAdapter.js import { ref, onMounted, onBeforeUnmount, nextTick } from vue export function useScreenAdapter(options {}) { const { designWidth 1920, designHeight 1080, mode contain, // contain | cover | stretch delay 120, } options const scale ref(1) const offsetX ref(0) const offsetY ref(0) const wrapperRef ref(null) let rafId null let timer null function getViewport() { // 用 documentElement 而不是 window.innerWidth // 后者会包含滚动条宽度导致计算结果偏大 const el document.documentElement return { width: el.clientWidth, height: el.clientHeight, } } function compute() { const { width: CW, height: CH } getViewport() if (!CW || !CH) return const scaleX CW / designWidth const scaleY CH / designHeight let nextScale 1 if (mode contain) { nextScale Math.min(scaleX, scaleY) } else if (mode cover) { nextScale Math.max(scaleX, scaleY) } scale.value nextScale offsetX.value (CW - designWidth * nextScale) / 2 offsetY.value (CH - designHeight * nextScale) / 2 syncStyle() } function syncStyle() { const el wrapperRef.value if (!el) return el.style.transform translate(${offsetX.value}px, ${offsetY.value}px) scale(${scale.value}) } function onResize() { if (rafId) cancelAnimationFrame(rafId) if (timer) clearTimeout(timer) // 先 rAF 保证下一帧前完成计算再用定时器兜底 rafId requestAnimationFrame(() { compute() }) timer setTimeout(() { compute() }, delay) } onMounted(() { nextTick(() { compute() }) window.addEventListener(resize, onResize, { passive: true }) }) onBeforeUnmount(() { window.removeEventListener(resize, onResize) if (rafId) cancelAnimationFrame(rafId) if (timer) clearTimeout(timer) }) return { wrapperRef, scale, offsetX, offsetY, } }有几点需要展开说明。为什么用documentElement.clientWidth而不是window.innerWidth因为innerWidth包含了滚动条的宽度。如果你的页面因为某个模块溢出意外出现了滚动条innerWidth会比可视区宽 15px 左右算出来的 scale 就偏大整个画面会往右下角轻微溢出。这个 bug 非常隐蔽尤其是在某些 Windows 机器上滚动条是常驻显示的你本地开发看不到客户现场就出问题。为什么既用 requestAnimationFrame 又用 setTimeout大屏项目里经常有浏览器全屏切换、F11 前后触发的 resize 事件这时候视口尺寸的变化不是同步的getViewport()拿到的可能还是旧值。rAF 保证在下一帧前重算一次定时器再延迟 120ms 兜底重算一次两保险。代价是缩放期间可能算两次但这个开销可以忽略。为什么要暴露 scale 出去因为图表、Canvas、地图这些组件需要知道当前缩放比例来做自己的适配后面章节会详细讲。3.3 在页面里怎么用主页面结构大概长这样template div classscreen-root !-- 背景层不参与缩放 -- div classscreen-bg/div !-- 缩放层 -- div classscreen-stage refwrapperRef header classscreen-header某某指挥中心数据看板/header main classscreen-main section classcol-left PanelBox title实时概览 LineChart / /PanelBox /section section classcol-center ... /section section classcol-right ... /section /main /div /div /template script setup import { useScreenAdapter } from /adapt/useScreenAdapter const { wrapperRef, scale } useScreenAdapter({ designWidth: 1920, designHeight: 1080, mode: contain, }) /script style scoped langscss .screen-root { position: fixed; inset: 0; overflow: hidden; background: #050a1a; } .screen-bg { position: absolute; inset: 0; background: radial-gradient(circle at 50% 50%, #0b1a3a 0%, #050a1a 70%); } .screen-stage { position: absolute; top: 0; left: 0; width: 1920px; height: 1080px; transform-origin: left top; will-change: transform; } .screen-header { height: 88px; line-height: 88px; text-align: center; font-size: 34px; letter-spacing: 4px; color: #7fd8ff; } /stylesreen-stage上的will-change: transform是个小优化它会让浏览器提前把这个图层提升到 GPU 合成层缩放过程中不会出现闪烁。4. 图表与第三方组件在大屏下的适配细节4.1 ECharts 在缩放容器里为什么会糊这是大屏项目里被问得最多的一个问题。现象是整体布局都对但 ECharts 的线条和文字看起来发虚放大看有明显的锯齿。原因在于transform: scale()是一个视觉层面的缩放它作用于元素渲染完成之后的位图结果。ECharts 默认用 Canvas 渲染echarts.init的时候会按照容器的实际像素尺寸创建一个 canvas 位图比如 600×400 的 canvas然后 CSS 把它拉伸到 1200×800 显示。位图被放大两倍自然糊。注意chart.resize()解决不了这个问题。因为resize()读的是offsetWidth和offsetHeight这两个属性不受 transform 影响所以 ECharts 认为容器还是 600×400重新绘制一遍还是 600×400 的位图再被拉伸。很多人在window.onresize里疯狂调chart.resize()一点效果都没有就是这个原因。有三种解决方案我按推荐程度排序。方案一用 SVG 渲染器。echarts.init(el, null, { renderer: svg })。SVG 是矢量图形被 CSS 放大不会失真字一直是清晰的。代价是数据量大的时候比如几千个点的折线图性能会明显下降因为 DOM 节点数量会暴涨。我的经验是大屏上的图表数据量一般都不大几十到几百个点用 SVG 完全没问题而且线条在 4K 屏上更锐利。方案二按缩放比例补偿 devicePixelRatio。const chart echarts.init(el, null, { renderer: canvas, devicePixelRatio: window.devicePixelRatio * scale.value, })这样 ECharts 会按600 × scale的物理像素去绘制位图再被 CSS 放大scale倍最终显示效果就是 1:1 的物理像素不会糊。但这个方案有个坑scale 变化之后需要把 chart 销毁重建否则 dpr 还是旧值。而且devicePixelRatio设太大比如 4K 屏 dpr2scale2加起来是 4会导致显存占用飙升页面卡顿。方案三图表容器不参与缩放。把 ECharts 放在缩放层外面用绝对定位和计算好的坐标摆放。这个方案最清晰但最复杂因为你要为每个图表单独算位置跟设计稿的坐标体系脱节了改需求的时候非常痛苦。我不推荐。我现在的默认配置是方案一只有遇到大数据量的实时曲线才切方案二。// src/charts/useChart.js import { onMounted, onBeforeUnmount, shallowRef, nextTick } from vue import * as echarts from echarts export function useChart(getOption, options {}) { const chartRef shallowRef(null) const domRef shallowRef(null) let ro null function render() { if (!domRef.value) return if (!chartRef.value) { chartRef.value echarts.init(domRef.value, null, { renderer: svg, ...options, }) } chartRef.value.setOption(getOption(), true) } function resize() { // 注意这里只是让 echarts 重算内部布局 // 位图清晰度靠 renderer: svg 解决 chartRef.value?.resize() } onMounted(() { nextTick(render) ro new ResizeObserver(() resize()) if (domRef.value) ro.observe(domRef.value) window.addEventListener(resize, resize, { passive: true }) }) onBeforeUnmount(() { ro?.disconnect() window.removeEventListener(resize, resize) chartRef.value?.dispose() chartRef.value null }) return { domRef, chartRef, render, resize } }用shallowRef而不是ref存 ECharts 实例很关键。ref会递归地把整个实例对象变成响应式的ECharts 实例内部有大量循环引用和方法被 Proxy 包一层之后性能会断崖式下跌而且某些情况下会直接报错。这个坑我在早期项目里踩过图表一渲染页面就卡死。4.2 表格、地图和大屏轮播的适配处理表格自适应宽度。Element Plus 的el-table在大屏里用默认是内容撑开的宽高比一变列就错位。我的做法是给每列显式指定宽度或者用min-width配合table-layout: fixed。如果列数固定直接在el-table-column上写死width因为整块屏是等比缩放的写死反而最精确。el-table :datalist stylewidth: 100% :header-cell-style{ background: transparent } el-table-column propname label名称 width220 / el-table-column propvalue label数值 width160 alignright / el-table-column propstatus label状态 min-width120 / /el-table另外el-table默认的边框色、hover 背景色在深色大屏上会显得很突兀需要整体覆写主题。我一般会在全局样式里改 CSS 变量:root { --el-table-border-color: rgba(64, 158, 255, 0.25); --el-table-tr-bg-color: transparent; --el-table-header-bg-color: rgba(64, 158, 255, 0.08); --el-table-text-color: #cfe6ff; }地图。如果大屏上有中国地图或者省市地图用 ECharts 的 geo 组件配合 SVG renderer 是最好的组合矢量渲染在高分屏上边缘非常干净。需要注意地图的center和zoom是针对坐标系算的不受 CSS 缩放影响所以不用额外处理只要保证地图容器本身在缩放层内就行了。大屏轮播。常见的三种轮播列表自动向上滚动、顶部标题横向滚动、整屏多页切换。前两种用 CSS 动画 定时器就能搞定不依赖第三方库性能也好。第三种整屏切换我建议直接用一个currentPage变量配合transition不要引入 swiper 这类库。template div classscreen-stage refwrapperRef transition namepage-fade modeout-in component :ispages[current] :keycurrent / /transition /div /template script setup import { ref, onMounted, onBeforeUnmount } from vue const pages [PageA, PageB, PageC] const current ref(0) const duration 15000 let timer null function startLoop() { timer setInterval(() { current.value (current.value 1) % pages.length }, duration) } onMounted(startLoop) onBeforeUnmount(() clearInterval(timer)) /script滚动列表的实现要点是把列表内容复制一份拼在后面然后用 CSStransform: translateY()做匀速位移位移到一份内容的高度时瞬间重置为 0视觉上就是无缝循环。千万别用setInterval去改scrollTop那个在低端机器上会抖。keyframes scroll-up { from { transform: translateY(0); } to { transform: translateY(-50%); } } .scroll-list__inner { animation: scroll-up 20s linear infinite; :hover { animation-play-state: paused; } }translateY(-50%)的前提是内容重复了一份所以滚动一半正好是一个循环。4.3 字体模糊和渲染性能的两难等比缩放在字体上有个副作用如果 scale 是小数比如 0.6667字体会被压缩笔画会糊。这在设计稿 1920、实际屏幕 1366 的笔记本上特别常见。我的处理方式是给字体单独加一点补偿。对于标题这类大字号缩放后基本没问题因为笔画粗对于正文 14px 的小字压缩后确实难看清这时候基本上说明这块屏不适合展示这块大屏应该跟需求方沟通换屏或者减少文字量。性能方面transform: scale()是 GPU 加速的本身开销极小但有一个例外里面如果有大量box-shadow、filter: blur()的元素缩放时会有明显的掉帧。我见过一个大屏上均匀分布了八十个带发光效果的圆点每个都有box-shadow和animation在客户的老机器上跑只有 20 帧。解决方案是把这些装饰性元素合并成一张图或者用::before伪元素画一个渐变圆代替box-shadow。实操心得大屏开发阶段建议在 Chrome DevTools 的 Rendering 面板里打开 Paint flashing 和 Frame Rendering Stats一眼就能看出哪个模块在疯狂重绘。很多看起来“莫名其妙卡”的问题打开这个面板就真相大白了。5. 打包上线后布局异常的排查手册5.1 常见问题速查表这一节按“现象 → 原因 → 解法”整理都是我实际遇到过并且解决了的问题。现象大概率原因处理方式上线后整体偏右下右下角溢出用了window.innerWidth把滚动条宽度算进去了改用documentElement.clientWidth同时给 body 加overflow: hidden缩放后出现滚动条缩放层尺寸写死但父容器没设overflow: hidden根容器position: fixed; inset: 0; overflow: hidden两边有黑边且背景发白背景层没铺满或者是浏览器默认底色背景层单独铺满视口根节点设深色底色图表模糊Canvas 位图被 CSS 拉伸ECharts 改renderer: svg点击位置偏移事件坐标没除以 scale(e.clientX - rect.left) / scale字体忽大忽小同时存在 rem 和 px 两套单位统一单位大屏里全部用 px首屏闪现未缩放的原始尺寸首次计算在 DOM 渲染之后onMounted里nextTick计算或给容器加visibility: hidden直到算完切换全屏后错位F11 触发的 resize 事件时机特殊resize 里同时用 rAF 和延时重算4K 屏上文字边缘发虚dpr 和 scale 叠加走 SVG renderer或按需补偿 dpr5.2 两个真实排查案例案例一打包后布局整体错位。开发环境好好的npm run build之后部署到服务器客户打开发现整个大屏往左上角缩了一块右下留出一条。我一开始怀疑是 CSS 压缩把transform属性优化掉了查了半天没结果。后来发现问题出在打包后的代码执行时机上。我用的是路由懒加载大屏页面的组件是异步加载的onMounted执行的时候父级容器可能还没完全布局完成documentElement.clientWidth拿到的是不含滚动条的错误值。解决方案是在路由进入前先算一次然后在组件onMounted里再算一次同时给根容器加一个min-width兜底。修复代码大概是这样// router/index.js router.afterEach(() { // 路由切换完成后再触发一次自适应计算 window.dispatchEvent(new Event(resize)) })案例二ECharts tooltip 位置偏了。大屏上有几块饼图鼠标移上去 tooltip 出现在离鼠标很远的地方。这个问题的根因是 ECharts 计算 tooltip 位置用的是鼠标的视口坐标而图表自己在缩放的坐标系里两套坐标系对不上。ECharts 5 之后对这种情况做了一定处理但在 transform 缩放的容器里仍然有问题。我的解法是关掉 follow 模式给 tooltip 一个固定位置tooltip: { trigger: item, confine: true, position: right, // 或者用一个函数返回固定坐标 }固定位置在大屏场景下反而更好看因为观众离得远tooltip 跟着鼠标跑反而看不清。注意排查大屏问题时一定不要只看自己的开发机。找一台和现场同型号、同分辨率的机器或者在浏览器里手动设置成目标分辨率再复现一次。我吃过好几次“本机没问题、现场全崩”的亏最典型的一次是客户的屏幕缩放比例设成了 125%Windows 的 DPI 缩放和浏览器的 devicePixelRatio 叠加算出来的尺寸全都对不上。6. 性能与工程化让大屏长期跑得稳6.1 大屏长时间运行的稳定性处理大屏跟普通网页有个本质区别它是要 7×24 小时常驻显示的。这就带来两个额外要求内存不能持续增长数据要能自动刷新。内存泄漏最常见的来源是定时器和事件监听没清理。setInterval拉取数据的定时器、window.addEventListener(resize)、ECharts 实例、ResizeObserver这些全部要在onBeforeUnmount里清干净。我习惯在写组件的时候最后再回头过一遍所有的addEventListener逐个确认有没有对应的remove。数据刷新方面我给每个数据源单独配置刷新间隔不要全站在一个定时器里一起刷否则会出现同一秒内几十个请求并发把后端打挂。我的做法是写一个简单的调度器// src/utils/poller.js const tasks new Map() export function registerPoll(key, fn, interval) { if (tasks.has(key)) return // 先立即执行一次避免首屏空白 fn() const id setInterval(fn, interval) tasks.set(key, id) } export function clearAllPoll() { tasks.forEach((id) clearInterval(id)) tasks.clear() }然后在路由离开或者页面卸载的时候调用clearAllPoll()。这样既不会漏清也不会重复注册。6.2 多分辨率兼容和现场部署的收尾工作最后说说现场部署。大屏项目到现场之后总有一些事是你控制不了的屏幕真实分辨率、操作系统的缩放比例、浏览器版本、甚至屏幕拼接带来的边框几毫米的物理黑边会让视觉中心偏移。我一般会在项目里留一个隐藏的调试面板按快捷键调出来可以直接改基准尺寸和缩放模式方便现场快速微调。// 快捷键 Ctrl Shift D 打开调试面板 window.addEventListener(keydown, (e) { if (e.ctrlKey e.shiftKey e.key D) { showDebugPanel.value !showDebugPanel.value } })调试面板里至少有这几个可调项基准宽高、缩放模式contain/cover、偏移量微调。有一次客户的拼接屏物理边框有 8mm视觉中心整体偏了就是靠这个面板现场微调偏移量解决的五分钟搞定不用重新打包部署。另外还要处理浏览器的全屏。大屏一般是全屏显示的用户不会手动按 F11所以要在页面上放一个全屏按钮用requestFullscreen触发同时监听fullscreenchange事件重算一次自适应。async function toggleFullscreen() { if (!document.fullscreenElement) { await document.documentElement.requestFullscreen() } else { await document.exitFullscreen() } } document.addEventListener(fullscreenchange, () { // 退出/进入全屏后视口尺寸变化重算 window.dispatchEvent(new Event(resize)) })还有个小细节有些现场机器设置了自动休眠或者屏保导致大屏半夜黑掉。这个在前端层面能做的不多但可以通过定时播放一个极低透明度的动效来保持页面活跃或者干脆在部署文档里写清楚让运维关掉休眠。我一般会把这条写进交付清单不然第二天早上客户打电话说“屏黑了”你还得跑一趟。我这些年做大屏最大的体会是自适应方案本身其实不复杂一二百行代码就能搞定真正花时间的是对各种边界情况的预判和处理。设计稿是一回事现场是另一回事中间差着分辨率、DPI 缩放、浏览器版本、拼接屏物理边框、甚至机房空调导致的设备温度差异。所以我现在做任何大屏项目第一件事就是跟需求方确认清楚这块屏在哪、多大、什么比例、谁维护、能不能让我远程连上去看一眼。把这几个问题问明白后面的开发能省掉一半的返工。至于代码层面把useScreenAdapter这个 Hook 用好把 ECharts 的渲染器选对把定时器和监听器清干净基本上就能覆盖九成以上的大屏场景了。
返回列表