响应式容器组件
对页面进行锁屏,始终保持一定的宽高比,常用于数据可视化开发中。
Container 组件
<template>
<div id="container" :ref="refName">
<template v-if="ready">
<slot></slot>
</template>
</div>
</template>
<script>
import { ref, getCurrentInstance, onMounted, onUnmounted, nextTick } from 'vue'
import { debounce } from '../../shared/util.js'
export default {
name: 'Container',
props: {
options: Object
},
setup(ctx) {
const refName = 'container';
const width = ref(0);
const height = ref(0);
const originalWidth = ref(0);
const originalHeight = ref(0);
const ready = ref(false);
let context, dom, observer;
const initSize = () => {
return new Promise((resolve, reject) => {
nextTick(() => {
dom = context.$refs[refName];
if (ctx.options && ctx.options.width && ctx.options.height) {
width.value = ctx.options.width;
height.value = ctx.options.height;
} else {
width.value = dom.clientWidth;
height.value = dom.clientHeight;
}
if (!originalWidth.value || !originalHeight.value) {
originalWidth.value = window.screen.width;
originalHeight.value = window.screen.height;
}
resolve();
})
});
}
const updateSize = () => {
if (width.value && height.value) {
dom.style.width = `${width.value}px`;
dom.style.height = `${height.value}px`;
} else {
dom.style.width = `${originalWidth.value}px`;
dom.style.height = `${originalHeight.value}px`;
}
}
const updateScale = () => {
const currentWidth = document.body.clientWidth;
const currentHeight = document.body.clientHeight;
const realWidth = width.value || originalWidth.value;
const realHeight = height.value || originalHeight.value;
const widthScale = currentWidth / realWidth;
const heightScale = currentHeight / realHeight;
dom.style.transform = `scale(${widthScale}, ${heightScale})`;
}
const onResize = async () => {
await initSize();
updateScale();
}
const initMutationObserver = () => {
const MutationObserver = window.MutationObserver;
observer = new MutationObserver(onResize);
observer.observe(dom, {
attributes: true,
attributeFilter: ['style'],
attributeOldValue: true
});
}
const removeMutationObserver = () => {
if (observer) {
observer.disconnect();
observer.takeRecords();
observer = null;
}
}
onMounted(async () => {
ready.value = false;
context = getCurrentInstance().ctx;
await initSize();
updateSize();
updateScale();
window.addEventListener('resize', onResize);
initMutationObserver();
ready.value = true;
});
onUnmounted(() => {
window.removeEventListener('resize', debounce(0, onResize));
removeMutationObserver();
});
return {
refName,
ready
}
}
}
</script>
<style lang="scss" scoped>
#container {
position: fixed;
top: 0;
left: 0;
overflow: hidden;
transform-origin: left top;
z-index: 999;
}
</style>
测试用例
<template>
<div class="home">
<Container :options="{ width: 3840, height: 2160 }">
<div class="test">123</div>
</Container>
</div>
</template>
<script>
export default {
name: 'Home'
}
</script>
<style lang="scss" scoped>
.test {
font-size: 64px;
color: red;
}
</style>