前端大屏自适应方法总结

张开发
2026/4/16 4:23:58 15 分钟阅读

分享文章

前端大屏自适应方法总结
方法一缩放scale1.app.vue组件代码用于app组件缩放会作用于整项目也可以单独用于大屏页面template ElConfigProvider :localelocale div classinner :style{ width: ${styleTransform.width}px, height: ${styleTransform.height}px, transform: styleTransform.transform } router-view/router-view /div /ElConfigProvider /template script setup import {reactive, onMounted, onBeforeUnmount} from vue; import {ElConfigProvider} from element-plus; import zhCn from element-plus/dist/locale/zh-cn.mjs; import {useRouter} from vue-router; // 定义 Element Plus 的语言环境 const locale zhCn; const router useRouter(); // 使用 reactive 创建响应式对象保存视口的宽度、高度和变换样式 const styleTransform reactive({ width: 1920, // 视口的初始宽度 height: 1080, // 视口的初始高度 transform: scaleY(1) scaleX(1) translate(-50%, -50%), // 初始变换样式 }); // 生命周期钩子在组件挂载时设置初始缩放并设置窗口调整大小时的处理函数 onMounted(() { setScale(); // 组件挂载时设置缩放 window.addEventListener(resize, setScale); // 监听窗口调整大小事件 }); // 在组件销毁时移除窗口调整大小事件监听器 onBeforeUnmount(() { window.removeEventListener(resize, setScale); // 移除窗口调整大小事件监听器 }); // 根据窗口大小计算缩放比例 const getScale () { const w window.innerWidth / styleTransform.width; // 计算宽度缩放比例 const h window.innerHeight / styleTransform.height; // 计算高度缩放比例 return {x: w, y: h}; }; // 根据计算的缩放比例更新变换样式 const setScale () { const scale getScale(); // 获取当前缩放比例 styleTransform.transform scaleY(${scale.y}) scaleX(${scale.x}) translate(-50%, -50%); }; /script style scoped .inner { transform-origin: 0 0; /* 设置缩放的原点为元素的左上角 */ position: fixed; /* 固定定位使元素相对于视口的位置不变 */ left: 50%; /* 将元素水平居中 */ top: 50%; /* 将元素垂直居中 */ transition: 0.3s; /* 为缩放样式的变化添加平滑过渡效果 */ } /style缺点使用此方法后页面不能有地图cesium等js库形状会受缩放影响从而影响地图的交互效果1.1解决地图受缩放影响以及把缩放功能抽离成组件用插槽的方式使用缩放组件在需要缩放的内容外面套上这层组件script setup langts import { computed, onBeforeUnmount, onMounted, reactive } from vue interface Props { width?: number height?: number debounce?: number disabled?: boolean } const props definePropsProps() function parsePx(key: string, fallback: number) { const value import.meta.env[key] if (!value) return fallback const num Number(value) return Number.isFinite(num) num 0 ? num : fallback } const envWidth parsePx(VITE_SCREEN_DESIGN_WIDTH, 1920) const envHeight parsePx(VITE_SCREEN_DESIGN_HEIGHT, 1080) const envEnabled import.meta.env.VITE_SCREEN_FIT ! false const designWidth computed(() props.width ?? envWidth) const designHeight computed(() props.height ?? envHeight) const fitEnabled computed(() envEnabled !props.disabled) const debounceMs computed(() props.debounce ?? 50) const styleTransform reactive({ width: designWidth.value, height: designHeight.value, scaleX: 1, scaleY: 1, transform: scaleY(1) scaleX(1) translate(-50%, -50%), }) let timer: number | null null function setScale() { if (!fitEnabled.value) return styleTransform.width designWidth.value styleTransform.height designHeight.value const x window.innerWidth / styleTransform.width const y window.innerHeight / styleTransform.height styleTransform.scaleX x styleTransform.scaleY y styleTransform.transform scaleY(${y}) scaleX(${x}) translate(-50%, -50%) } function handleResize() { if (!fitEnabled.value) return if (timer) window.clearTimeout(timer) timer window.setTimeout(() { setScale() timer null }, debounceMs.value) } const innerStyle computed(() { if (!fitEnabled.value) { return { width: 100%, height: 100%, transform: none, --screen-scale-x: 1, --screen-scale-y: 1, --screen-inv-scale-x: 1, --screen-inv-scale-y: 1, } } return { width: ${styleTransform.width}px, height: ${styleTransform.height}px, transform: styleTransform.transform, --screen-scale-x: styleTransform.scaleX, --screen-scale-y: styleTransform.scaleY, --screen-inv-scale-x: 1 / styleTransform.scaleX, --screen-inv-scale-y: 1 / styleTransform.scaleY, } }) onMounted(() { if (!fitEnabled.value) return setScale() window.addEventListener(resize, handleResize) }) onBeforeUnmount(() { window.removeEventListener(resize, handleResize) if (timer) { window.clearTimeout(timer) timer null } }) /script template div classscreen-adaptive-shell div classscreen-adaptive-inner :class{ is-disabled: !fitEnabled } :styleinnerStyle slot / /div /div /template style scoped langscss .screen-adaptive-shell { width: 100vw; height: 100vh; margin: 0; overflow: hidden; position: relative; background: #0b1020; } .screen-adaptive-inner { transform-origin: 0 0;//左上角 position: fixed; left: 50%; top: 50%; transition: 0.3s; overflow: hidden; } .screen-adaptive-inner.is-disabled { position: relative; left: 0; top: 0; transition: none; } /style地图组件组件里面采用反缩放的方式抵消外层缩放的影响关键代码组件中.map-page类的width、height、transform、transform-origin这几个属性的设置script setup langts import { nextTick, onMounted, onUnmounted, ref } from vue import { useRouter } from vue-router import * as mars3d from mars3d const router useRouter() const mapRef refHTMLElement | null(null) let map: mars3d.Map | null null let graphicLayer: mars3d.layer.GraphicLayer | null null function addDemoPoint() { if (!map) return graphicLayer new mars3d.layer.GraphicLayer() map.addLayer(graphicLayer) const point new mars3d.graphic.PointEntity({ name: 观测点A, position: [117.224496, 31.791702, 1200], style: { pixelSize: 14, color: #00eaff, outline: true, outlineColor: #ffffff, outlineWidth: 2, clampToGround: false, }, attr: { id: point-a-001, name: 观测点A, lng: 117.224496, lat: 31.791702, status: 在线, desc: 用于演示地图点位点击弹框, }, popup: (event: { graphic?: { attr?: Recordstring, unknown } }) { const attr event?.graphic?.attr ?? {} return div stylemin-width:220px; h4 stylemargin:0 0 8px;点位信息/h4 divID${String(attr.id ?? -)}/div div名称${String(attr.name ?? -)}/div div经度${String(attr.lng ?? -)}/div div纬度${String(attr.lat ?? -)}/div div状态${String(attr.status ?? -)}/div div说明${String(attr.desc ?? -)}/div /div }, }) graphicLayer.addGraphic(point) } const resizeMap () { const mapWithResize map as unknown as { resize?: () void } | null mapWithResize?.resize?.() } const onResize () { resizeMap() } onMounted(() { const el mapRef.value if (!el) return map new mars3d.Map(el.id, { scene: { center: { lat: 31.791702, lng: 117.224496, alt: 7852906, heading: 0, pitch: -90, }, }, basemaps: [{ type: tdt, layer: img_d, show: true }] as any, }) addDemoPoint() // 确保在容器布局稳定后触发一次重算避免初始偏移 nextTick(() { resizeMap() requestAnimationFrame(() { resizeMap() }) }) window.addEventListener(resize, onResize) }) onUnmounted(() { window.removeEventListener(resize, onResize) if (graphicLayer map) { map.removeLayer(graphicLayer, true) graphicLayer null } if (map) { map.destroy() map null } }) /script template div classmap-container div classmap-page div idmars3d-container refmapRef classmars3d-box / /div div classtoolbar el-button typeprimary link clickrouter.push({ name: home }) 返回首页 /el-button /div /div /template style scoped langscss .map-container{ width:100%; height: 100%; position: relative; } .map-page { --map-bleed: 28px; // 外扩像素按需调大/调小 position: relative; width: calc(100% * var(--screen-scale-x, 1)); height: calc(100% * var(--screen-scale-y, 1)); min-height: 0; background: #000; overflow: hidden; // 裁掉超出区域达到“遮边”效果 transform: scale( var(--screen-inv-scale-x, 1), var(--screen-inv-scale-y, 1) ); transform-origin: left top;//左上角 } .toolbar { position: absolute; z-index: 2; left: 12px; top: 12px; } .mars3d-box { position: absolute; inset: calc(-1 * var(--map-bleed)); width: auto; height: auto; min-height: 0; } //去掉弹框滚动条 :deep(.mars3d-popup-content) { max-height: none !important; overflow-y: hidden !important; } /style方法二vhvw1.思路用视口高度除以设计稿高度算出一个比值再用比值乘以对应元素在设计稿中的高度,算出不同视口高度下该元素的高度算宽度也是这个思路)。2.具体实现1定义函数style langscss scoped //vw : 相对于视口的宽度1vw 等于视口宽度的1%总视口宽度为100vw //vh : 相对于视口的高度 1vh 等于视口高度的1%总视口高度为100vh //算出当前视口高度和设计稿高度的比值 $heightRatio: calc(100vh / 1080); //算出当前视口宽度和设计稿宽度的比值 $widthRatio: calc(100vw / 1920); //定义函数传入设计稿中对应元素的高度根据比值算出不同视口高度下该元素的高度 function heightRatio_($contentHeight) { //return calc($heightRatio * $contentHeight); } //定义函数传入设计稿中对应元素的宽度根据比值算出不同视口宽度下该元素的宽度 function widthRatio_($contentWidth) { return calc($widthRatio * $contentWidth); }2.在编写样式代码时引用函数1横向属性如果需要设置一个数值都用widthRatio_函数比如width、margin-left、margin-right、padding-left、padding-right。2纵向属性如果需要设置一个数值都用heightRatio_函数比如height、margin-top、margin-bottom、padding-top、padding-bottom、line-height3字体的大小可根据情况选用这两个函数的其中一个我一般用heightRatio_让字体大小随着视口高度的变化而变化4函数中传入的参数就是该属性在设计稿中的实际数值3.特殊说明使用此方法需特别注意在编写样式的时候元素的布局元素的边距宽高的设置尤为重要建议都按照设计稿的来不然容易出现滚动条。

更多文章