<!--
|
* @Author: wei
|
* @description: 大屏自适应容器组件
|
* @LastEditTime: 2022-09-09 16:42:40
|
-->
|
<template>
|
<!-- <section class="screen-box" :style="boxStyle"> -->
|
<div class="screen-wrapper" ref="screenWrapper" :style="wrapperStyle">
|
<slot></slot>
|
</div>
|
<!-- </section> -->
|
</template>
|
<script>
|
/**
|
* 防抖函数
|
* @param {T} fn
|
* @param {number} delay
|
* @return
|
*/
|
function debounce(fn, delay) {
|
let timer = null;
|
return function (...args) {
|
timer = setTimeout(
|
() => {
|
typeof fn === "function" && fn.apply(null, args);
|
clearTimeout(timer);
|
},
|
delay > 0 ? delay : 100
|
);
|
};
|
}
|
|
export default {
|
name: "VScaleScreen",
|
props: {
|
width: {
|
type: [String, Number],
|
default: 1920,
|
},
|
height: {
|
type: [String, Number],
|
default: 1080,
|
},
|
fullScreen: {
|
type: Boolean,
|
default: false,
|
},
|
autoScale: {
|
type: [Object, Boolean],
|
default: true,
|
},
|
selfAdaption: {
|
type: Boolean,
|
default: true,
|
},
|
delay: {
|
type: Number,
|
default: 500,
|
},
|
boxStyle: {
|
type: Object,
|
default: () => ({}),
|
},
|
wrapperStyle: {
|
type: Object,
|
default: () => ({}),
|
},
|
},
|
data() {
|
return {
|
currentWidth: 0,
|
currentHeight: 0,
|
originalWidth: 0,
|
originalHeight: 0,
|
onResize: null,
|
observer: null,
|
};
|
},
|
watch: {
|
selfAdaption(val) {
|
if (val) {
|
this.resize();
|
this.addListener();
|
} else {
|
this.clearListener();
|
this.clearStyle();
|
}
|
},
|
},
|
computed: {
|
screenWrapper() {
|
return this.$refs["screenWrapper"];
|
},
|
},
|
methods: {
|
initSize() {
|
return new Promise((resolve, reject) => {
|
// console.log("初始化样式");
|
//给父元素设置 overflow:hidden
|
this.screenWrapper.parentNode.style.overflow = "hidden";
|
this.screenWrapper.parentNode.scrollLeft = 0;
|
this.screenWrapper.parentNode.scrollTop = 0;
|
|
this.$nextTick(() => {
|
// region 获取大屏真实尺寸
|
if (this.width && this.height) {
|
this.currentWidth = this.width;
|
this.currentHeight = this.height;
|
} else {
|
this.currentWidth = this.screenWrapper.clientWidth;
|
this.currentHeight = this.screenWrapper.clientHeight;
|
}
|
// endregion
|
// region 获取画布尺寸
|
if (!this.originalHeight || !this.originalWidth) {
|
this.originalWidth = window.screen.width;
|
this.originalHeight = window.screen.height;
|
}
|
// endregion
|
resolve();
|
});
|
});
|
},
|
updateSize() {
|
if (this.currentWidth && this.currentHeight) {
|
this.screenWrapper.style.width = `${this.currentWidth}px`;
|
this.screenWrapper.style.height = `${this.currentHeight}px`;
|
} else {
|
this.screenWrapper.style.width = `${this.originalWidth}px`;
|
this.screenWrapper.style.height = `${this.originalHeight}px`;
|
}
|
},
|
handleAutoScale(scale) {
|
if (!this.autoScale) return;
|
const screenWrapper = this.screenWrapper;
|
const domWidth = screenWrapper.clientWidth;
|
const domHeight = screenWrapper.clientHeight;
|
const currentWidth = document.body.clientWidth;
|
const currentHeight = document.body.clientHeight;
|
screenWrapper.style.transform = `scale(${scale},${scale}) `;
|
let mx = Math.max((currentWidth - domWidth * scale) / 2, 0);
|
let my = Math.max((currentHeight - domHeight * scale) / 2, 0);
|
if (typeof this.autoScale === "object") {
|
// @ts-ignore
|
!this.autoScale.x && (mx = 0);
|
// @ts-ignore
|
!this.autoScale.y && (my = 0);
|
}
|
// console.log({
|
// mx,
|
// my,
|
// currentWidth,
|
// currentHeight,
|
// domWidth,
|
// domHeight,
|
// scale,
|
// });
|
this.screenWrapper.style.margin = `${my}px ${mx}px`;
|
},
|
updateScale() {
|
const screenWrapper = this.screenWrapper;
|
// 获取真实视口尺寸
|
const currentWidth = document.body.clientWidth;
|
const currentHeight = document.body.clientHeight;
|
// 获取大屏最终的宽高onResize
|
const realWidth = this.currentWidth || this.originalWidth;
|
const realHeight = this.currentHeight || this.originalHeight;
|
// 计算缩放比例
|
const widthScale = currentWidth / realWidth;
|
const heightScale = currentHeight / realHeight;
|
// console.log({currentWidth, currentHeight,realWidth,realHeight});
|
|
// 若要铺满全屏,则按照各自比例缩放
|
if (this.fullScreen) {
|
screenWrapper.style.transform = `scale(${widthScale},${heightScale})`;
|
return false;
|
}
|
// 按照宽高最小比例进行缩放
|
const scale = Math.min(widthScale, heightScale);
|
this.handleAutoScale(scale);
|
},
|
initMutationObserver() {
|
const screenWrapper = this.screenWrapper;
|
const observer = (this.observer = new MutationObserver(() => {
|
this.onResize();
|
}));
|
|
observer.observe(screenWrapper, {
|
attributes: true,
|
attributeFilter: ["style"],
|
attributeOldValue: true,
|
});
|
},
|
clearListener() {
|
window.removeEventListener("resize", this.onResize);
|
},
|
addListener() {
|
window.addEventListener("resize", this.onResize);
|
},
|
clearStyle() {
|
// console.log("清除");
|
const screenWrapper = this.screenWrapper;
|
screenWrapper.parentNode.style.overflow = "auto";
|
|
screenWrapper.style = "";
|
},
|
async resize() {
|
if (!this.selfAdaption) {
|
return;
|
}
|
await this.initSize();
|
this.updateSize();
|
this.updateScale();
|
},
|
},
|
mounted() {
|
this.onResize = debounce(() => {
|
this.resize();
|
}, this.delay);
|
this.$nextTick(() => {
|
if (this.selfAdaption) {
|
this.resize();
|
this.addListener();
|
}
|
});
|
},
|
beforeDestroy() {
|
this.clearListener();
|
// this.observer.disconnect()
|
},
|
};
|
//
|
</script>
|
|
<style scoped>
|
.screen-box {
|
overflow: hidden;
|
background-size: 100% 100%;
|
background: #000;
|
width: 100vw;
|
height: 100vh;
|
}
|
|
.screen-wrapper {
|
transition-property: all;
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
transition-duration: 500ms;
|
position: relative;
|
overflow: hidden;
|
z-index: 100;
|
transform-origin: left top;
|
}
|
</style>
|