☰
Vue大屏自适应方案:transform scale落地与ECharts补偿
2026/9/30 5:28:19 网站建设 项目流程

第一次给指挥中心做投屏,我用 Vue 写的大屏在本地开发机(1920×1080)上分毫不差,接到现场一块 3840×2160 的屏幕上,整个页面缩在左上角只占四分之一,右边和下边是两大片黑。当时我以为是浏览器的问题,折腾了半天才发现,是页面宽度被写死成 1920px,而视口变成了 3840px。后来这些年,从会议室一体机到展厅竖屏,从数据看板到视频监控墙,Vue 大屏自适应这件事我基本踩遍了能踩的坑,也试过 rem、vw/vh、transform scale、容器查询四条技术路线。

这篇东西不打算给你一个"复制粘贴就能用"的万能公式,因为大屏适配从来就没有万能公式。我想做的是把这几套方案的边界说清楚——它们各自在什么场景下成立、在哪里会崩、崩了以后怎么补救,然后把我们线上跑了两年多的那套 transform scale 方案,连同配套的图表像素补偿、弹层定位修正、非 16:9 妥协策略,完整地摊开讲一遍。不管你是刚接到第一个大屏需求的新人,还是已经被模糊的 ECharts 和乱飞的弹窗折磨过的老手,应该都能从里面捞到点能直接落地的东西。

1. 大屏适配到底在适配什么:先把物理场景和设计稿对齐

1.1 三种大屏场景,物理特性完全不同

"大屏"这个词在不同团队嘴里含义差得很远,方案选型从第一步就该分叉。我接触最多的是这三类:

第一类是会议室或展厅的一体机,常见 65 到 98 寸,面板物理分辨率 3840×2160,跑 Windows 或者安卓系统。这种场景最麻烦的变量不是分辨率,而是Windows 的显示缩放。当系统缩放设成 150% 时,浏览器全屏后的window.innerWidth只有 2560,你的 4K 面板实际按 2560 的逻辑像素在排版。同一个项目在 A 会议室正常,搬到 B 会议室就错位,八成是两台的显示缩放设置不一样。

第二类是指挥中心的 LED 拼接墙,物理分辨率可能是 3×3 拼接的 5760×3240。但实际交付时,绝大多数项目走的是拼接处理器方案:一台主机输出 1920×1080,由硬件把这一路信号拉伸铺满整墙。这意味着你的页面设计稿还是 1920×1080,只是最终会被物理放大三倍左右。这里有个容易被忽略的点:1px 的发丝线物理放大后会变成三四个像素宽,看着发糊;反过来如果整体缩小,1px 的线可能直接消失。所以给拼接墙做的设计稿,线条我会统一用 2px 起步。

第三类是展会竖屏,1080×1920 那种立式广告机。这种基本算另一套产品了,布局逻辑跟横屏完全不同,我一般会单独出一版设计稿,而不是指望同一套代码翻转。还有一类是超宽屏,比如 21:9 甚至 32:9 的弧形屏,常见于地铁站或者展厅。

1.2 第一步不是写代码,是量屏幕

我现在的固定动作是:拿到项目后,第一时间要求在目标设备上打开浏览器控制台,把这三个值读出来,写进项目的 README 里。

console.log({ innerWidth: window.innerWidth, innerHeight: window.innerHeight, dpr: window.devicePixelRatio, screenW: screen.width, screenH: screen.height })

为什么要这么较真?因为window.innerWidth和screen.width经常不是一回事,设备像素比devicePixelRatio又决定了你的 canvas 该开多大位图。这三个数决定了后面所有的取舍,比讨论用什么框架重要得多。

顺手把设计稿的尺寸也在这个环节敲定。设计稿宽高一旦确定,就是整个项目的"世界坐标系",后面所有布局都以它为基准,不允许中途改。

1.3 等比缩放、弹性布局、混合策略的分界线

真正决定方案走向的,其实是一个业务问题:你的大屏能不能接受留白或者变形?

  • 指挥中心监控墙:绝对不能变形,地图、雷达、拓扑图一旦拉伸就失真,留黑边可以接受。
  • 展会宣传屏:也不能变形,但也不能留黑边,因为太难看了,所以倾向于背景铺满、内容等比。
  • 内部数据看板:可以接受弹性布局,左右两栏随屏幕宽度伸缩反而更好用。

这三种业务诉求,对应三条完全不同的技术路线。很多团队一上来就问"哪个方案最好",其实应该先问"这块屏允许我留白吗",答案出来,方案基本就锁定一半了。

2. 四套主流方案的实测对比:rem、vw/vh、transform scale、容器查询

2.1 每套方案到底在做什么

先说 rem。核心就一行 JS:把根元素的字号按视口宽度动态算出来,然后所有尺寸都用 rem。

function setRootFontSize() { const w = document.documentElement.clientWidth document.documentElement.style.fontSize = (w / 1920) * 100 + 'px' }

设计稿上 200px 宽的元素,写 2rem 就行,配合postcss-pxtorem自动转换,改造成本很低。它的好处是像素是像素,没有 GPU 拉伸那回事,字体边缘永远锐利。

再说 vw/vh。这是纯 CSS 路线,用postcss-px-to-viewport把 px 全部转成 vw,连 JS 都不用写。构建期完成转换,运行时零开销,理论上最优雅。代价是它对"非等比"这件事完全没有办法——屏幕比例一变,元素要么挤要么散。

第三条是 transform scale,也就是大家都在用的那套:整块内容按设计稿写死 1920×1080,外层套一个缩放容器,按视口算一个缩放比,靠 CSS 变换整体放大缩小。优点是开发体验极好,设计稿上量多少就写多少,所见即所得,出了一点像素级偏差都看得见。缺点集中在渲染质量上,后面单独开一章讲。

第四条是容器查询@container。给容器声明container-type: inline-size,然后内部按容器宽度写断点。它是目前唯一"组件级自适应"的原生方案,适合做设计系统,但对于追求像素级还原的大屏,说实话性价比不高——大屏要的是精确复刻,不是响应式优雅降级。

2.2 四套方案的关键指标对照

维度remvw/vhtransform scale容器查询
像素级还原一般较差优秀较差
改造成本中低低高
第三方组件兼容需单独处理需单独处理天然兼容天然兼容
非等比屏幕表现会变形会变形可留白可延展取决于断点
字体清晰度锐利锐利有模糊风险锐利
canvas/视频质量正常正常需补偿正常
最小字号限制会受影响会受影响不受影响不受影响

表格里有两个点值得展开。一个是第三方组件兼容:rem 和 vw 方案下,Element Plus 这类组件库内部的 px 不会跟着你的设计稿走,一个 32px 高的输入框在 4K 屏上看着像颗芝麻,你得额外配置插件的转换范围,风险不小。transform scale 方案下,组件库的东西连带你自己的布局一起被缩放了,视觉比例天然一致。

另一个是最小字号限制。rem 方案在视口宽度很小时,根字号会被算得很小,此时部分浏览器环境的字号下限会跳出来兜底,把文字强制拉大,导致排版直接错乱。我确实在几个中文环境的机器上撞到过这个问题,排查起来相当费劲。transform scale 方案因为字号本身写的是设计稿值(比如 14px),缩放发生在渲染层,反而不受字号下限影响——这是我后来坚定选它的一个重要原因。

2.3 我的选型判断表

把上面的东西压缩成几条经验:

  • 需求里有"像素级还原"四个字,直接上 transform scale,别犹豫。
  • 项目是通用后台,只是偶尔投到大屏上看看,用弹性布局 + 少量断点,别搞缩放。
  • 团队前端规范成熟、组件库自研、追求长期可维护,可以考虑容器查询,但别指望它一晚上搞定。
  • 项目里全是第三方图表、地图、视频,而且必须清晰,做好像素补偿的心理准备再上 scale。

3. transform scale 方案的完整落地链路

3.1 容器结构的四个关键属性

先看 DOM 结构。外层负责铺满视口和裁切,内层负责承载设计稿尺寸的内容。

<div class="screen-wrapper"> <div class="screen-stage" :style="stageStyle"> <router-view /> </div> </div>

外层样式只有三件事:铺满、裁切、铺底色。

.screen-wrapper { position: fixed; inset: 0; overflow: hidden; background: #0a1428; // 与设计稿边缘同色,避免留白突兀 }

内层的关键是四个属性:固定宽高对应设计稿、绝对定位到左上角、变换原点设在左上角、由 JS 动态注入变换矩阵。

.screen-stage { position: absolute; top: 0; left: 0; width: 1920px; height: 1080px; transform-origin: 0 0; }

这里必须强调一次:transform-origin选 0 0 还是 center center,决定了你后面的偏移计算方式,两者不能混用。我见过有人在样式里写了transform-origin: center center,JS 里又按左上角原点算偏移,结果页面永远对不齐,查了一整天。

3.2 useScreenScale:把缩放逻辑封装成一个组合式函数

Vue3 里我会把它做成 composable,Vue2 项目可以改写成 mixin 或者全局指令,思路完全一样。

// useScreenScale.ts import { ref, computed, onMounted, onBeforeUnmount } from 'vue' export const DESIGN_WIDTH = 1920 export const DESIGN_HEIGHT = 1080 type Mode = 'contain' | 'cover' | 'width' | 'height' export function useScreenScale( designWidth = DESIGN_WIDTH, designHeight = DESIGN_HEIGHT, mode: Mode = 'contain' ) { const scale = ref(1) const offsetX = ref(0) const offsetY = ref(0) let rafId = 0 function calc() { const vw = document.documentElement.clientWidth const vh = document.documentElement.clientHeight let s = 1 if (mode === 'contain') s = Math.min(vw / designWidth, vh / designHeight) if (mode === 'cover') s = Math.max(vw / designWidth, vh / designHeight) if (mode === 'width') s = vw / designWidth if (mode === 'height') s = vh / designHeight scale.value = s offsetX.value = (vw - designWidth * s) / 2 offsetY.value = (vh - designHeight * s) / 2 } function schedule() { cancelAnimationFrame(rafId) rafId = requestAnimationFrame(calc) } const stageStyle = computed(() => ({ transform: `translate(${offsetX.value}px, ${offsetY.value}px) scale(${scale.value})` })) onMounted(() => { calc() window.addEventListener('resize', schedule) window.addEventListener('orientationchange', schedule) }) onBeforeUnmount(() => { cancelAnimationFrame(rafId) window.removeEventListener('resize', schedule) window.removeEventListener('orientationchange', schedule) }) return { scale, offsetX, offsetY, stageStyle, recalc: calc } }

三个细节解释一下。第一,为什么用requestAnimationFrame而不是防抖定时器?拖动窗口时resize会连续触发几十上百次,如果每次都同步读写布局,会引发布局抖动。rAF 把计算压到下一帧之前,一次渲染周期只算一次,比定时器更贴合渲染节奏,也不会出现"松手后还延迟几百毫秒才对齐"的观感。

第二,translate必须写在scale前面。CSS 的变换按矩阵顺序右乘,写在前面的变换作用在最终的视觉位移上,不会被后面的缩放放大。如果你写成scale(s) translate(x, y),那个 x 和 y 就会被乘上 s,偏移量彻底错位。这个坑我踩过,当时现象是"屏幕越宽,页面偏得越离谱"。

第三,translate的偏移值用未缩放的视口像素算,所以offsetX = (vw - 1920 * s) / 2直接就是视觉上的居中偏移,不用再除以 s。

3.3 双轴策略:让侧栏弹性、让主视觉等比

上面这套是"整体等比缩放",适合监控墙。但如果需求是"左右两侧列表随屏幕变宽多显示几行内容,中间地图保持比例",整体缩放就不合适了。我用的变体是这样:缩放比仍按高度算,但舞台宽度反向撑开。

const s = vh / designHeight scale.value = s stageWidth.value = vw / s // 关键:把视口宽度换算回设计稿坐标系 offsetX.value = 0 offsetY.value = 0

舞台的高度固定 1080,宽度变成一个动态值(比如 2600)。此时页面里左右两侧的列表容器用flex: 1自动吃掉多余宽度,中间的地图容器给固定宽度、保持设计比例。缩放回去之后,整块内容正好铺满屏幕,没有黑边,两侧列表还多了可用空间。

这个思路我在多个指挥中心项目里用过,配合display: grid的三栏布局非常顺。要注意的是:宽度撑开之后,所有依赖"设计稿 1920 宽"的绝对定位元素都会错位,所以这套方案下布局必须用 flex 或 grid 相对布局,不能用绝对定位堆。

4. 缩放的代价:ECharts、视频流、3D 场景的像素比补偿

4.1 图表为什么不跟着放大:一次典型的误判

transform scale 方案上线后第一个报障,十有八九是图表变糊。而且诡异的地方在于:你监听窗口 resize,调用了chart.resize(),图表明明"响应"了,还是糊。

根因在于CSS transform 不改变布局尺寸。图表容器的clientWidth永远是设计稿里写的那个值(比如 600px),无论外面把它放大到了 1.5 倍还是 3 倍。ECharts 内部判定"尺寸没变",于是既不重绘也不调整 canvas 位图。浏览器拿着一张 600×300 的位图,直接拉到 1800×900 显示,结果就是文字边缘糊成一片,折线像毛边。

所以正确的补偿方向不是让图表 resize,而是让 canvas 的位图分辨率跟上放大的倍数。

4.2 三种补偿写法的取舍

第一种,在初始化时把缩放比乘进设备像素比:

import * as echarts from 'echarts' function createChart(el: HTMLElement, scale: number) { return echarts.init(el, undefined, { renderer: 'canvas', // 关键:把 CSS 缩放倍数补进 dpr devicePixelRatio: Math.min(window.devicePixelRatio * scale, 3) }) }

注意我加了Math.min(..., 3)做上限。4K 屏上devicePixelRatio本身可能是 2,再乘个 2 倍缩放就是 4,canvas 面积直接膨胀到 16 倍,显存和绘制耗时都会爆掉,大屏一卡就全卡。压到 3 是性能和效果的平衡点,实测 3 倍位图在全屏观看距离下已经看不出模糊。

第二种,缩放比变化时重建实例。ECharts 官方的resize()不接受devicePixelRatio参数,也就是说窗口大小变了、缩放比变了之后,旧的 dpr 不会自动更新。我的做法是在缩放比变化超过阈值(比如 0.05)时,把实例 dispose 掉重新 init:

watch(scale, (next, prev) => { if (Math.abs(next - prev) > 0.05) { chartRef.value?.dispose() chartRef.value = createChart(elRef.value!, next) bindOption(chartRef.value) } })

窗口拖拽过程中缩放比会连续变化,所以要加防抖,一般 200 毫秒足够。重建实例有成本,但窗口尺寸变化本身就不是高频事件,可以接受。

第三种是直接改内部 painter 的 dpr,属于非公开 API,我不建议在正式项目里用,升级一次版本可能就废了。这里提一句只是让你在别人的代码里看到时不至于懵。

4.3 视频流、WebGL 和地图的额外处理

视频流在大屏里极其常见,无论是 HLS 切片播放还是 WebRTC 低延迟推流,最后落到 DOM 上都是一个<video>元素。它跟 canvas 不太一样:现代浏览器对视频纹理的缩放处理更聪明,放大后看着是"柔"而不是"糊"。真正的问题往往出在源头分辨率上——你拿一路 720p 的流,放到 4K 拼接墙上放大六倍,怎么补偿都没用。我的处理顺序是:先确认能不能拿到更高分辨率的源,源没问题再考虑适配层的优化。

WebGL 场景就更直接了,Three.js 和 Cesium 这类库必须显式告诉渲染器像素比:

const renderer = new THREE.WebGLRenderer({ antialias: true }) renderer.setPixelRatio(Math.min(window.devicePixelRatio * scale, 2)) renderer.setSize(width, height, false)

具体倍数按现场机器显卡能力定,大屏主机普遍用的是核显或者入门独显,像素比给高了帧率掉到 20 以下,看着比模糊更难受。我的经验值是 2 封顶。

顺便说一句 1px 边框。设计稿里 1px 的描边,在 2 倍缩放下变成 2 个物理像素,在拼接墙上再被硬件放大 3 倍,就是 6 个像素宽,粗得刺眼。我现在的做法是:给大屏做的设计稿,边框统一 2px 起,分割线用背景色块或者渐变模拟,别用发丝线。

5. 缩放容器里的定位陷阱:fixed 弹层、鼠标坐标与表格滚动

5.1 一个反直觉的规范:transform 会改变 fixed 的包含块

这个坑我认为是 transform scale 方案里最隐蔽的一个。按 CSS 规范,只要元素的transform不是none,它就会成为其后代position: fixed元素的包含块。也就是说,缩放舞台内部写position: fixed的遮罩层,不再相对视口定位,而是相对这块 1920×1080 的舞台定位。

表现是什么?你在缩放容器里手写了一个全屏加载遮罩,position: fixed; inset: 0,本地开发看着好好的,因为本地视口刚好接近设计稿尺寸,偏差看不出来。一上大屏,遮罩只盖住了舞台范围内的一部分,边缘露出一圈没遮住的内容。

知道原理之后处理起来很简单:在缩放容器内部,一律不用 fixed,需要铺满就用position: absolute; inset: 0。覆盖范围跟着舞台走,缩放之后视觉上就是铺满的。

5.2 坐标换算:从 clientX 到设计稿坐标

第二个高频问题是鼠标坐标。只要你做的是自定义绘制、热点区域、跟随鼠标的提示框,就一定会遇到。event.clientX给的是视口坐标,而你的业务逻辑是按设计稿坐标系写的,中间差着一个偏移和一个缩放比。

function toStageCoord(e: MouseEvent) { const wrapper = document.querySelector('.screen-wrapper') as HTMLElement const rect = wrapper.getBoundingClientRect() const x = (e.clientX - rect.left - offsetX.value) / scale.value const y = (e.clientY - rect.top - offsetY.value) / scale.value return { x, y } }

这里的offsetX和offsetY就是useScreenScale里算出来的那两个值。如果舞台是"宽度延展"模式,偏移为 0,公式退化成只除以缩放比。

还有个更隐蔽的坑:getBoundingClientRect()返回的是变换之后的矩形。你写el.getBoundingClientRect().width,在缩放 2 倍的屏幕上拿到的是 1200,而不是元素本来的 600。要拿设计稿尺寸,用el.offsetWidth或el.clientWidth。我在一个拖拽排序需求里被这个坑了大半天,手柄一直吸不准位置,最后发现是尺寸拿错了坐标系,除以缩放比之后就正常了。

5.3 组件库浮层的实际表现与我的处理偏好

拿 Element Plus 举例。它的下拉、气泡、弹窗默认会传送到body下,因为这些浮层挂在缩放容器外面,所以它们不受缩放影响——位置计算是对的,但尺寸是 1:1 的。在 2 倍缩放的界面上,一个 14px 字体、32px 高的下拉框显得特别小,和周围被放大过的界面明显不在一个层里,视觉上很割裂。

我的处理偏好是:把浮层留在缩放容器内部。Element Plus 的下拉支持:teleported="false",对话框支持:append-to-body="false",关掉传送之后,浮层就跟着容器一起缩放,尺寸和位置同时对齐。代价是要注意两件事——一是容器的overflow: hidden不能把浮层裁掉(大屏本身不滚动,一般没影响);二是浮层的层级要在容器内部足够高,别被别的卡片盖住。

表格也是重灾区。el-table的自动列宽计算基于clientWidth,缩放不影响它,所以列宽算得是对的,这点可以放心。真正别扭的是滚动条——滚动条宽度会被一起缩放,2 倍缩放下原本 8px 的滚动条变成 16px 视觉宽度,看着很粗;反过来缩小时可能细到不好拖。大屏一般是鼠标操作或者遥控操作,滚动条本身用得少,我通常直接美化掉,用隐藏滚动条 + 自定义指示器替代。

6. 非 16:9 屏幕的三种妥协策略与实现

6.1 居中留白:最安全也最保守

设计稿 16:9,屏幕 21:9,等比缩放必然在左右各留一条黑边,这是contain模式。它的好处是零风险,任何比例都能正确显示,内容永远不变形。指挥中心、演示汇报这类场景我首选它。

要减轻黑边的观感,可以做两件事。一是底色对齐:把外层 wrapper 的背景色设成跟设计稿边缘一样的深色,黑边和内容边界就模糊掉了。二是背景往外延:设计稿边缘那圈装饰性的光效、网格、粒子,不要用普通 DOM 画,改用一张background-size: cover的背景图层铺满整个视口,内容层再等比缩放叠在上面。视觉上几乎看不出有留白。

6.2 高度铺满 + 宽度延展:内容型大屏的最优解

如果业务能接受"两侧多显示点内容",那第 3.3 节讲的宽度延展模式是最舒服的。核心就是把舞台宽度设成vw / scale,然后让布局容器的弹性生效。

具体到三栏布局:

.stage-layout { display: grid; grid-template-columns: 1fr 1200px 1fr; // 中间地图锁死,两侧弹性 height: 1080px; gap: 24px; }

中间地图锁 1200px,保证比例不变;两侧用1fr,屏幕越宽列表越长。16:9 的屏幕上侧栏各 336px,21:9 的屏幕上能到 600 多 px,正好多放两行数据。这套方案实际用下来,客户满意度明显高于留黑边。

要注意的是,如果侧栏里是图表,图表宽度变了之后必须重新 resize,而且因为舞台宽度是按设计稿坐标系算的,图表的容器宽度变化是真实的布局变化,ECharts 的resize()这次是有效的——这也是它跟第 4 章场景的区别:同样的图表组件,在整体缩放模式下要靠 dpr 补偿,在宽度延展模式下靠 resize 就能解决。两套逻辑别搞混。

6.3 内容等比 + 背景铺满:展会场景的常用组合

展会屏幕不允许出现任何黑边,但内容也不能变形,剩下的办法就是把背景层和内容层拆开。背景层固定铺满视口,内容层按contain缩放居中。这样即使两侧有区域没放内容,观众看到的也是完整的背景,不会觉得"这屏没做完"。

内容层如果实在偏窄,还可以做一档轻微的补偿:把缩放比在contain和cover之间插值。比如scale = minRatio + (maxRatio - minRatio) * 0.2,允许内容被拉伸 20%,肉眼几乎看不出来,但能吃掉一大截留白。这个比例我是凭经验给的,超过 0.3 圆形的指示灯会明显变椭圆,慎用。

7. 上线前的检查清单:打包、定时器与长跑稳定性

7.1 本地正常、打包后布局异常的三个常见原因

这个现象太常见了,我整理了三个高频原因,按排查优先级排:

第一,CSS 压缩工具吃掉了 calc 里的空格。早期版本的压缩器会把calc(100% - 16px)压成calc(100%-16px),减号两侧没有空格,整个表达式直接失效,元素宽度变成 auto,布局全乱。这类问题在开发环境不会出现,因为 dev 阶段不做压缩。排查方法很简单:打开生产环境的 CSS,搜一下calc(,看两边有没有空格。

第二,静态资源路径不对。打包后部署到子目录,背景图 404 了,看起来就像"布局错乱",其实只是背景丢了。检查构建配置里的资源基础路径,以及 CSS 里引用的图片是否走了正确的解析规则。

第三,插件配置在生产和开发之间不一致。比如 px 转 rem/vw 的插件只在开发配置里挂了,生产构建漏了,本地一切正常,发布后所有尺寸按原始 px 渲染。这个最容易排查,搜一下打包产物里有没有出现 vw 单位就知道了。

7.2 7×24 长跑的稳定性问题

大屏项目跟普通后台最大的不同是:它要连续跑几个月不关。这类问题里,内存泄漏排第一。

最常见的泄漏点就是图表实例。每分钟刷新数据、重建一次 ECharts 实例,如果没调dispose(),一天的累积足够把内存吃到几个 G。我的做法是给每个图表封一个统一的生命周期:onBeforeUnmount里dispose,切换数据源时先clear()再setOption,尽量不重建实例。同理,setInterval和addEventListener都必须在卸载时清掉。

第二个问题是浏览器后台节流。用户切走标签页或者屏幕上弹了个系统窗口,浏览器会把定时器大幅降频,requestAnimationFrame干脆完全暂停。切回来的时候,如果你的轮播逻辑是靠"每次 tick 加一"实现的,就会出现一段诡异的静止和一次突兀的跳跃。我的处理方式是基于时间戳算进度,而不是靠次数累加,并且在visibilitychange里主动暂停和恢复动画。

document.addEventListener('visibilitychange', () => { if (document.hidden) { pauseAll() } else { resumeFromTimestamp(Date.now()) } })

第三是Windows 显示缩放带来的跨机器差异。部署脚本里我没法控制这个,所以我会在页面启动时把innerWidth、devicePixelRatio这些值打到控制台,工作人员截个图发过来,我就能判断现场环境是否异常。

7.3 上线前的巡检清单

检查项判断标准常见后果
目标设备视口尺寸innerWidth/innerHeight 是否与预期一致整体缩放比算错
显示缩放设置是否为 100%,或已纳入计算跨机器表现不一致
图表位图补偿全屏后文字是否锐利图表发虚、折线毛边
浮层层级与定位下拉、弹窗是否跟随缩放尺寸割裂或位置偏移
1px 线条视觉宽度是否可接受过粗或消失
定时器清理路由切换后是否持续增长内存持续上涨
后台恢复切走再切回是否正常动画错乱
生产构建产物calc 空格、vw 单位、资源路径布局突然错乱

这张表我基本每次交付前都会过一遍,其中前三项每次必查。

最后分享一个我现在的固定动作:在项目根目录放一份screen-info.md,写下目标屏幕的物理分辨率、逻辑视口、设备像素比、显示缩放设置、适配模式和缩放比计算公式,再附上一张现场实拍图。这玩意儿看着不起眼,但半年后要加一块新屏、或者换个人接手的时候,能省掉整整一天的现场排查。我上一个项目就是因为没这东西,为了搞清楚"为什么客户说字变小了"来回跑了三趟。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询