概述
本方案为前端大屏项目提供统一的屏幕自适应能力,通过一个包装组件实现以下核心功能:
- 自动旋转:当屏幕高度大于宽度(竖屏设备)时,强制将内容旋转90°,保持横屏布局。
- 等比例缩放:以设计稿宽度为基准,确保画面宽度完全展示,高度不足时居中裁切(亦可调整为高度适配)。
- 响应式监听:窗口尺寸变化时实时更新旋转状态和缩放比例。
- 框架通用:提供 Vue 3、React 18、Angular 15+ 三大主流框架的封装实现,任何页面、插件组件(如 ECharts、DataV)只需包裹即可生效。
核心原理
1. 旋转逻辑
- 判定条件:
window.innerHeight > window.innerWidth - 旋转策略:当判定为竖屏时,将内容容器旋转 90° 并平移归位,使之填满视口。
容器尺寸互换:宽度 = 原屏幕高度 (100vh),高度 = 原屏幕宽度 (100vw)。 - 关键 CSS(当旋转时):
旋转后原点仍在左上角,需上移自身高度(即width: 100vh;
height: 100vw;
transform: rotate(90deg) translateY(-100%);
transform-origin: top left;100%的原容器高度 =100vw)才能对齐视口。
2. 缩放适配
- 设计稿尺寸:通常为
1920×1080(可按项目配置)。 - 缩放比例:
scale = 当前容器宽度 / 设计稿宽度 由于旋转后容器宽度等于实际可见宽度,直接以此为基准即可实现“长边完全展示”。 - 缩放层定位:内部缩放层保持设计稿原始尺寸,通过
transform: scale()缩放并居中显示,外层overflow: hidden隐藏溢出。
3. 响应式处理
监听 window.resize事件(可增加防抖优化)。或使用 ResizeObserver监听父容器(非全屏场景)。
组件实现
一、Vue 3 版本(Composition API + 插槽)
<!-- AdaptScreen.vue -->
<template>
<div class="adapt-outer">
<div class="adapt-inner" :class="{ rotated: isRotated }">
<div class="scale-layer" :style="scaleStyle">
<slot />
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
const props = defineProps({
designWidth: { type: Number, default: 1920 },
designHeight: { type: Number, default: 1080 },
})
const isRotated = ref(false)
const scale = ref(1)
function update() {
const w = window.innerWidth
const h = window.innerHeight
isRotated.value = h > w
const containerW = isRotated.value ? h : w
scale.value = containerW / props.designWidth
}
const scaleStyle = computed(() => ({
width: `${props.designWidth}px`,
height: `${props.designHeight}px`,
transform: `scale(${scale.value})`,
}))
onMounted(() => {
update()
window.addEventListener('resize', update)
})
onUnmounted(() => window.removeEventListener('resize', update))
</script>
<style scoped>
.adapt-outer {
width: 100vw;
height: 100vh;
overflow: hidden;
background: #000; /* 可按需修改 */
}
.adapt-inner {
width: 100%;
height: 100%;
}
.rotated {
width: 100vh;
height: 100vw;
transform: rotate(90deg) translateY(-100%);
transform-origin: top left;
}
.scale-layer {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>使用示例:
<template>
<AdaptScreen :designWidth="1920" :designHeight="1080">
<YourDashboardContent />
</AdaptScreen>
</template>
<script setup>
import AdaptScreen from './components/AdaptScreen.vue'
</script>二、React 版本(Hooks + JSX)
// AdaptScreen.jsx
import { useState, useEffect } from 'react'
function useAdaptScreen(designWidth = 1920, designHeight = 1080) {
const [size, setSize] = useState({ w: window.innerWidth, h: window.innerHeight })
useEffect(() => {
const handleResize = () => setSize({ w: window.innerWidth, h: window.innerHeight })
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
const rotated = size.h > size.w
const containerWidth = rotated ? size.h : size.w
const scale = containerWidth / designWidth
const outerStyle = {
width: '100vw',
height: '100vh',
overflow: 'hidden',
background: '#000',
}
const innerStyle = rotated
? {
width: '100vh',
height: '100vw',
transform: 'rotate(90deg) translateY(-100%)',
transformOrigin: 'top left',
}
: { width: '100%', height: '100%' }
const layerStyle = {
width: designWidth,
height: designHeight,
transform: `scale(${scale})`,
position: 'absolute',
top: '50%',
left: '50%',
marginTop: -designHeight / 2,
marginLeft: -designWidth / 2,
}
return { outerStyle, innerStyle, layerStyle }
}
export default function AdaptScreen({ designWidth = 1920, designHeight = 1080, children }) {
const { outerStyle, innerStyle, layerStyle } = useAdaptScreen(designWidth, designHeight)
return (
<div style={outerStyle}>
<div style={innerStyle}>
<div style={layerStyle}>{children}</div>
</div>
</div>
)
}使用示例:
import AdaptScreen from './AdaptScreen'
function App() {
return (
<AdaptScreen designWidth={1920} designHeight={1080}>
<Dashboard />
</AdaptScreen>
)
}三、Angular 版本(Standalone Component)
// adapt-screen.component.ts
import { Component, Input, OnInit, OnDestroy } from '@angular/core';
@Component({
selector: 'app-adapt-screen',
standalone: true,
template: `
<div class="adapt-outer">
<div class="adapt-inner" [class.rotated]="isRotated">
<div
class="scale-layer"
[style.width.px]="designWidth"
[style.height.px]="designHeight"
[style.transform]="'scale(' + scale + ')'"
>
<ng-content></ng-content>
</div>
</div>
</div>
`,
styles: [`
.adapt-outer {
width: 100vw;
height: 100vh;
overflow: hidden;
background: #000;
}
.adapt-inner {
width: 100%;
height: 100%;
}
.rotated {
width: 100vh;
height: 100vw;
transform: rotate(90deg) translateY(-100%);
transform-origin: top left;
}
.scale-layer {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
`]
})
export class AdaptScreenComponent implements OnInit, OnDestroy {
@Input() designWidth = 1920;
@Input() designHeight = 1080;
isRotated = false;
scale = 1;
private resizeHandler = () => this.update();
ngOnInit(): void {
this.update();
window.addEventListener('resize', this.resizeHandler);
}
ngOnDestroy(): void {
window.removeEventListener('resize', this.resizeHandler);
}
private update(): void {
const w = window.innerWidth;
const h = window.innerHeight;
this.isRotated = h > w;
const containerW = this.isRotated ? h : w;
this.scale = containerW / this.designWidth;
}
}使用示例(在任意组件模板中):
<app-adapt-screen [designWidth]="1920" [designHeight]="1080">
<app-dashboard></app-dashboard>
</app-adapt-screen>提示:若使用传统 NgModule 方式,请移除 standalone: true 并将组件在模块中声明。
插件与第三方组件集成
所有第三方可视化库(如 ECharts、DataV、AntV 等)均可直接作为子内容插入,只需确保其布局基于设计稿尺寸(如 1920×1080)进行绝对定位或百分比布局。
示例(Vue + DataV):
<AdaptScreen>
<dv-full-screen-container>
<dv-border-box>内容</dv-border-box>
</dv-full-screen-container>
</AdaptScreen>React 示例:
<AdaptScreen>
<FullScreenContainer>
<BorderBox>内容</BorderBox>
</FullScreenContainer>
</AdaptScreen>可选增强配置
- 防抖处理
为 resize事件增加防抖(如debounce100ms),减少高频计算压力。 - 高度适配模式
若希望高度完全展示(宽度裁切),将缩放计算改为: scale = containerHeight / designHeight,同时调整旋转容器逻辑。 - 安全边距
可将 designWidth/designHeight设置得略大于实际内容尺寸,防止边缘被裁切。 - 监听父容器而非窗口
使用 ResizeObserver替代window.resize,适用于非全屏的大屏嵌入场景。 - CSS 变量支持
将设计尺寸和背景色等定义为 CSS 变量,便于主题化定制。
注意事项
旋转后的内容会交换宽高,内部元素的鼠标事件坐标可能需要适配(通常无需额外处理,因为缩放层已基于设计稿)。 若页面中存在绝对定位的弹出层(如模态框),需确保其也挂载在缩放层内部或手动应用相同变换。 在极端分辨率下,缩放可能导致字体模糊,建议使用矢量图标和 SVG 资源。 旋转时浏览器可能对部分 CSS 动画有性能影响,建议优先使用 transform和opacity实现动画。
总结
通过将旋转、缩放逻辑抽象为单一组件,所有页面和插件均可零侵入实现大屏自适应。本文档提供的三大框架实现可直接复制使用,降低多分辨率适配成本,保障大屏项目视觉一致性。
夜雨聆风