💥别再抄网上的Scale缩放代码了!50行源码教你写一个永不翻车的大屏适配

2 阅读2分钟

📚 《vfit.js大屏适配指南》系列

第1篇:拒绝rem计算!Vue3大屏适配,我是这样做的

第2篇:还在用rem做大屏适配?用vfit.js一键搞定,告别改稿8版的噩梦

第3篇:别再抄网上的Scale缩放代码了!50行源码教你写一个永不翻车的大屏适配

第4篇:大屏元素永远对不齐?一招搞定所有绝对定位,再也不用调像素到凌晨

90%的大屏开发者,都在网上抄过这段"万能缩放代码":

window.addEventListener('resize', () => {
  const scale = window.innerWidth / 1920
  document.body.style.transform = `scale(${scale})`
})

我也抄了3年,直到上个月做政务大屏,客户把大屏嵌进了iframe里——整个页面直接缩成了一团。 改了3天没搞定,直到扒开vfit的源码才发现:原来我们手写的Scale,从根上就错了。 今天把这50行核心源码拆解给你,看完你再也不用抄任何人的代码。


一、手写Scale的3个必死场景,你一定踩过

别以为你的代码现在能用,只要遇到下面任意一个场景,立刻翻车:

  1. iframe嵌套:窗口大小没变,容器大小变了,你的缩放代码完全没反应
  2. 微前端:大屏作为子应用嵌入主系统,坐标和缩放全部错乱
  3. 弹窗内大屏:点击按钮弹出一个大屏弹窗,打开就是变形的

这些问题的根源只有一个:你监听的是window.resize,而不是大屏容器本身的大小变化


二、第一斧:抛弃window.resize,拥抱ResizeObserver

这是vfit和所有手写Scale最本质的区别。 window.resize只能监听整个浏览器窗口的大小变化,而ResizeObserver可以监听任意DOM元素的尺寸变化。

核心代码:

// 创建一个观察者
const observer = new ResizeObserver((entries) => {
  // 拿到容器的实际宽高
  const { width, height } = entries[0].contentRect
  // 计算正确的缩放比例
  const scale = calculateScale(width, height)
  // 应用缩放
  applyScale(container, scale)
})

// 监听大屏容器,而不是window
observer.observe(document.querySelector('#app'))

就这几行代码,直接解决了iframe、微前端、弹窗内大屏的所有适配问题。 不管你的大屏放在哪里,只要它的容器尺寸变了,就会自动触发缩放。


三、第二斧:一行公式,搞定所有比例计算

很多人写Scale,比例计算都是错的,这就是为什么你的大屏会变形。 错误写法:

// 只按宽度缩放,高度会被拉伸,圆形变椭圆
const scale = width / designWidth

正确写法(vfit同款):

// auto模式:比较容器和设计稿的宽高比,谁更"瘦"听谁的
const calculateScale = (containerWidth, containerHeight, designWidth, designHeight) => {
  return containerWidth / containerHeight < designWidth / designHeight
    ? containerWidth / designWidth
    : containerHeight / designHeight
}

这个公式的神奇之处在于:

  • 1920×1080的设计稿,放到3840×2160的4K屏,自动按2倍缩放
  • 放到16:10的带鱼屏,自动留出上下黑边,绝不裁切,绝不变形
  • 放到iPad竖屏,自动留出左右黑边,所有元素比例完全一致

四、第三斧:GPU硬件加速拉满,告别卡顿

很多人大屏做出来卡成PPT,就是因为用了错误的CSS属性。 vfit只用了一行CSS,性能直接拉满:

const applyScale = (el, scale) => {
  // 用transform: scale,调用GPU硬件加速
  el.style.transform = `scale(${scale})`
  // 原点设为左上角,保证定位准确
  el.style.transformOrigin = 'left top'
  // 自动居中
  el.style.marginLeft = `${(window.innerWidth - el.offsetWidth * scale) / 2}px`
  el.style.marginTop = `${(window.innerHeight - el.offsetHeight * scale) / 2}px`
}

不用百分比,不用rem,直接操作GPU。 内部的ECharts、高德地图、Three.js完全感觉不到外面的变化,60帧丝滑运行。


五、终极福利:50行完整源码,直接复制用

把上面的代码整合起来,就是一个完整的、永不翻车的大屏适配工具:

export const createScale = (options) => {
  const { target, designWidth = 1920, designHeight = 1080 } = options
  const container = document.querySelector(target)
  if (!container) return

  const calculateScale = (w, h) => {
    return w / h < designWidth / designHeight ? w / designWidth : h / designHeight
  }

  const applyScale = (scale) => {
    container.style.transform = `scale(${scale})`
    container.style.transformOrigin = 'left top'
    container.style.marginLeft = `${(window.innerWidth - designWidth * scale) / 2}px`
    container.style.marginTop = `${(window.innerHeight - designHeight * scale) / 2}px`
  }

  const observer = new ResizeObserver((entries) => {
    const { width, height } = entries[0].contentRect
    applyScale(calculateScale(width, height))
  })

  observer.observe(container)
  // 初始化
  applyScale(calculateScale(window.innerWidth, window.innerHeight))

  return { destroy: () => observer.disconnect() }
}

// 使用方法
createScale({ target: '#app', designWidth: 1920, designHeight: 1080 })

六、如果你不想自己写,直接用vfit.js

上面的源码就是vfit的核心逻辑,vfit在此基础上做了更多优化:

  • 支持多种缩放模式(auto、width、height、contain、cover)
  • 内置防抖,避免频繁触发缩放
  • 提供FitContainer组件,解决绝对定位跑偏问题
  • 零依赖,打包后只有2KB

一行代码搞定所有适配:

npm install vfit
import { createApp } from 'vue'
import { createFitScale } from 'vfit'
import 'vfit/style.css'

createApp(App)
  .use(createFitScale({ designWidth: 1920, designHeight: 1080 }))
  .mount('#app')

💬 互动福利

你抄的Scale代码翻过哪些车?是iframe嵌套失效,还是微前端错乱? 评论区晒出你的血泪史,抽3人送手写Scale终极版源码+大屏常见分辨率适配速查表

评论+点赞+收藏,私信【大屏源码】,立即领取10套政务大屏经典布局模板。

官方资源直达:

vfit.js 官网:vfit.raychart.cn/

GitHub 仓库:github.com/v-plugin/vf…

下一篇预告:大屏元素永远对不齐?一招搞定所有绝对定位,再也不用调像素到凌晨。