<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>