使用大屏展示的时候有很多种场景,众多场景都是为了实现大屏自适应。
大屏,顾名思义,就是放在一个固定的屏幕上看的,即使你不做自适应,放在一个固定的屏幕上看也没啥问题,但是很多做大屏的是为了在PC端看,PC端屏幕又是参差不齐的,所以需要做自适应。
方案一:
使用transform的大小缩放来实现,我们先写一个缩放容器AutoScalContainer.vue,代码如下:
<template>
<div
class="auto-scal-container"
ref="AutoScalContainerRef">
<div
ref="DomRef"
class="auto-scal-container-inner">
<slot></slot>
</div>
</div>
</template>
<script>
/**
* 自动缩放容器
* 使用transform进行缩放
* */
import {
defineComponent,ref,getCurrentInstance,reactive,toRef,
computed,onMounted,onActivated,watch,
onBeforeUnmount,
} from "vue";
export default defineComponent({
props:{
width:{
type:Number,
default:1920,
},
height:{
type:Number,
default:1080,
},
/** 内部容器的宽高比例 */
ratio:{
type:Number,
default:1920 / 1080,
},
/**
* fit,原理同img的object-fit
* contain : 被替换的内容将被缩放,以在填充元素的内容框时保持其宽高比。
* cover : 被替换的内容在保持其宽高比的同时填充元素的整个内容框。如果对象的宽高比与内容框不相匹配,该对象将被剪裁以适应内容框。
* */
fit:{
type:String,
default:'contain',
},
},
emits:['onResizeScreen'],
setup(props,{emit}){
const DomRef = ref(null); //组件实例
const AutoScalContainerRef = ref(null); //组件实例
const dataContainer = reactive({
height:toRef(props,'height'),
width:toRef(props,'width'),
ratio:toRef(props,'ratio'),
fit:toRef(props,'fit'),
});
/** 是否是文档上 */
function isActive(){
if(!DomRef.value) return false;
return DomRef.value.getRootNode() === document;
}
/** 自动缩放 */
function autoResizeScreen(){
if(!AutoScalContainerRef.value) return;
if(!DomRef.value) return;
if(!isActive) return;
let rect = AutoScalContainerRef.value.getBoundingClientRect();
let clientWidth = rect.width;
let clientHeight = rect.height;
var width = dataContainer.width;
var height = dataContainer.height;
let left = 0;
let top = 0;
let scale = 0;
/** 使用外部传入的比例或者传入的宽高计算比例 */
let ratio = dataContainer.ratio || (width / height);
// 获取比例 可视化区域的宽高比与 屏幕的宽高比 来进行对应屏幕的缩放
if(dataContainer.fit == 'contain'){
if ((clientWidth / clientHeight) > ratio) {
scale = clientHeight / height;
top = 0;
left = (clientWidth - width * scale) / 2;
} else {
scale = clientWidth / width;
left = 0;
top = (clientHeight - height * scale) / 2;
}
}
if(dataContainer.fit == 'cover'){
if ((clientWidth / clientHeight) > ratio) {
scale = clientWidth / width;
} else {
scale = clientHeight / height;
}
}
// 防止组件销毁后还执行设置状态s
Object.assign(DomRef.value.style, {
transform: `scale(${scale})`,
left: `${left}px`,
top: `${top}px`,
});
/** 向外部通知已经计算缩放 */
emit('onResizeScreen');
}
/** 防抖 */
let timer_1;
function fnContainer(){
clearTimeout(timer_1);
// timer_1 = setTimeout(()=>{
autoResizeScreen();
// },16);
}
let timer = setInterval(()=>{
fnContainer();
},300);
onMounted(() => {
autoResizeScreen();
});
window.addEventListener('resize', fnContainer);
onBeforeUnmount(() => {
window.removeEventListener('resize', fnContainer);
window.clearInterval(timer);
});
return {
dataContainer,
DomRef,
AutoScalContainerRef,
};
},
});
</script>
<style lang="scss" scoped>
.auto-scal-container {
width: 100%;
height: 100%;
position: relative;
overflow: auto;
/** 隐藏滚动条 */
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
>.auto-scal-container-inner {
overflow: hidden;
transform-origin: left top;
z-index: 999;
width: max-content;
height: max-content;
position: absolute;
top: 0;
left: 0;
}
}
</style>
使用方式
<script>
/**
* 大屏主页面
* 采用缩放的形式进行适配,搭配rem的话很方便实用
* */
import { defineComponent,ref,getCurrentInstance,reactive,toRef, computed,onMounted,onActivated,watch } from "vue";
import AutoScalContainer from "@/components/AutoScalContainer.vue";
import ViewHead from "./components/ViewHead.vue";
import img_1 from "./assets/bg.png";
import img_2 from "./assets/1-1-bg.png";
import Box_1 from "./components/Box_1.vue";
import Box_2 from "./components/Box_2.vue";
import Box_3 from "./components/Box_3.vue";
import Box_4 from "./components/Box_4.vue";
import Box_5 from "./components/Box_5.vue";
import Box_6 from "./components/Box_6.vue";
import { useRoute } from "vue-router";
export default defineComponent({
name:'BigScreenView',
components: {
AutoScalContainer,
ViewHead,
Box_1,
Box_2,
Box_3,
Box_4,
Box_5,
Box_6,
},
setup(){
let route = useRoute();
const dataContainer = reactive({
loading:false,
img:{
img_1,
img_2,
},
fit:'contain',
});
watch(route,()=>{
let queryParams = route.query || {};
let fitMap = {
'cover':'cover',
'contain':'contain',
};
dataContainer.fit = fitMap[queryParams.fit] || 'contain';
},{
immediate:true,
});
return {
dataContainer,
};
},
});
</script>
<template>
<div class="big-screen-view">
<AutoScalContainer
:height="1080"
:width="1920"
:fit="dataContainer.fit">
<div
class="big-screen-view-container"
:style="{
'--bg-img-1':`url(${dataContainer.img.img_1})`,
'--bg-img-2':`url(${dataContainer.img.img_2})`,
}" >
<div class="head">
<ViewHead
title="数据可视化大屏展示"></ViewHead>
</div>
<div class="content">
<div class="top">
<Box_1></Box_1>
</div>
<div class="content">
<div class="left">
<div class="box">
<Box_2></Box_2>
</div>
<div class="box">
<Box_3></Box_3>
</div>
</div>
<div class="right">
<div class="box">
<Box_4></Box_4>
</div>
<div class="box">
<Box_5></Box_5>
</div>
</div>
</div>
</div>
<div class="centre-box">
<div class="v-height"></div>
<div class="container">
<Box_6></Box_6>
</div>
</div>
</div>
</AutoScalContainer>
</div>
</template>
<style lang="scss" scoped>
.big-screen-view{
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: #031045c7;
.big-screen-view-container{
width: 1920px;
height: 1080px;
background-color: rgb(169, 169, 169);
display: flex;
flex-direction: column;
background-image: var(--bg-img-1);
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
position: relative;
>.head{
height: 91px;
position: relative;
z-index: 2;
}
>.content{
display: flex;
flex-direction: column;
flex: 1 1 0;
width: 100%;
height: 0;
position: relative;
z-index: 2;
pointer-events: none;
>.top{
width: 100%;
height: 199px;
pointer-events: initial;
}
>.content{
display: flex;
flex-direction: row;
justify-content: space-between;
flex: 1 1 0;
width: 100%;
height: 0;
padding: 0 15px 15px 15px;
box-sizing: border-box;
>.left,>.right{
display: flex;
flex-direction: column;
>.box{
width: 100%;
flex: 1 1 0;
height: 0;
background-image: var(--bg-img-2);
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
margin: 0 0 15px 0;
pointer-events: initial;
&:last-child{
margin: 0;
}
}
}
>.left{
height: 100%;
width: 550px;
}
>.right{
height: 100%;
width: 550px;
}
}
}
>.centre-box{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
display: flex;
flex-direction: column;
>.v-height{
width: 100%;
height: 270px;
}
>.container{
flex: 1 1 0;
height: 0;
width: 100%;
}
}
}
}
</style>
好了,一个用transfor缩放的例子就完成了,使用rem的例子以后为大家讲解
源码
DEMO