<script setup name="autoScalContainer"> /** * 自动缩放容器,改变宽高进行缩放 * 容器内显示指定的比例 * */ import { computed, defineComponent, getCurrentInstance, onActivated, onBeforeUnmount, onMounted, reactive, ref, toRef, watch } from 'vue' const props = defineProps({ 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', }, }) const emits = defineEmits(['onResizeScreen']) 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 } const rect = AutoScalContainerRef.value.getBoundingClientRect() const clientWidth = rect.width const clientHeight = rect.height const width = dataContainer.width const height = dataContainer.height let domWidth = 0 let domHeight = 0 let domTop = 0 let domLeft = 0 /** 使用外部传入的比例或者传入的宽高计算比例 */ const ratio = dataContainer.ratio || width / height // 获取比例 可视化区域的宽高比与 屏幕的宽高比 来进行对应屏幕的缩放 if (dataContainer.fit == 'contain') { if (clientWidth / clientHeight > ratio) { domHeight = clientHeight domWidth = ratio * domHeight domTop = 0 domLeft = (clientWidth - domWidth) / 2 } else { domWidth = clientWidth domHeight = domWidth / ratio domTop = (clientHeight - domHeight) / 2 domLeft = 0 } } if (dataContainer.fit == 'cover') { if (clientWidth / clientHeight > ratio) { domWidth = clientWidth domHeight = domWidth / ratio } else { domHeight = clientHeight domWidth = ratio * domHeight } } // 防止组件销毁后还执行设置状态s Object.assign(DomRef.value.style, { width: `${domWidth}px`, height: `${domHeight}px`, top: `${domTop}px`, left: `${domLeft}px`, }) /** 向外部通知已经计算缩放 */ emits('onResizeScreen', { width: domWidth, height: domHeight, }) } /** 防抖 */ let timer_debouce function fnContainer() { clearTimeout(timer_debouce) // timer_debouce = setTimeout(()=>{ autoResizeScreen() // },16); } const timer = setInterval(() => { fnContainer() }, 300) onMounted(() => { autoResizeScreen() }) window.addEventListener('resize', fnContainer) onBeforeUnmount(() => { window.removeEventListener('resize', fnContainer) window.clearInterval(timer) }) </script> <template> <div ref="AutoScalContainerRef" class="auto-scal-container"> <div ref="DomRef" class="auto-scal-container-inner"> <slot /> </div> </div> </template> <style lang="scss" scoped> .auto-scal-container { width: 100%; height: 100%; position: relative; display: flex; justify-content: flex-start; align-items: flex-start; overflow: auto; /** 隐藏滚动条 */ -ms-overflow-style: none; scrollbar-width: none; &::-webkit-scrollbar { display: none; } > .auto-scal-container-inner { position: absolute; overflow: hidden; transform-origin: left top; width: 0; height: 0; top: 0; left: 0; } } </style>