1.节流函数
export const debounce = (fn, delay) => {
let timer = null
return function (...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
2.新建container文件目录并创建index.vue
引入节流函数
<template>
<div ref="containerTarget" class="container">
<template v-if="isReady">
<slot></slot>
</template>
</div>
</template>
<script setup>
import {nextTick, onMounted, onUnmounted, ref} from "vue";
import {debounce} from "@/utils/common.js"
const props = defineProps({
options: {
type: Object,
default: () => {
}
},
})
const isReady = ref(false)
const containerTarget = ref()
// 容器宽高
const containerPort = ref({
width: 0,
height: 0
})
// 视口的宽高
const viewport = ref({
width: 0,
height: 0
})
const onResize = async () => {
await initSize()
upDataScale()
}
let observer
// 改变样式运行
const initMutationObserver = () => {
observer = new window.MutationObserver(onResize)
observer.observe(containerTarget.value, {
attributes: true, // 属性
attributeOldValue: true,
attributeFilter: ['style']
})
}
const removeMutationObserver = () => {
if (observer) {
observer.disconnect()
observer.takeRecords()
observer = null
}
}
onMounted(async () => {
isReady.value = false
await initSize()
upDateSize()
upDataScale()
window.addEventListener("resize", debounce(onResize, 1000))
initMutationObserver()
isReady.value = true
})
onUnmounted(() => {
window.removeEventListener("resize", onResize)
removeMutationObserver()
})
const initSize = () => {
return new Promise((resolve) => {
nextTick(() => {
// 获取并设置容器宽高
if (props.options && props.options?.width && props.options.height) {
containerPort.value.width = props.options?.width
containerPort.value.height = props.options?.height
} else {
containerPort.value.width = containerTarget.value.clientWidth
containerPort.value.height = containerTarget.value.clientHeight
}
// 获取并设置视口宽高 0 = false 基于画布
if (!viewport.value.width || !viewport.value.height) {
viewport.value.width = window.screen.width
viewport.value.height = window.screen.height
}
// console.log(containerPort.value, window.screen)
resolve()
})
})
}
// 设置窗口大小
const upDateSize = () => {
if (containerPort.value.width && containerPort.value.height) {
containerTarget.value.style.width = `${containerPort.value.width}px`
containerTarget.value.style.height = `${containerPort.value.height}px`
} else {
containerTarget.value.style.width = `${viewport.value.width}px`
containerTarget.value.style.height = `${viewport.value.height}px`
}
}
// 计算视口改变时的缩放比例
const upDataScale = () => {
// 获取body视口的宽高去计算
const currentWidth = document.body.clientWidth
const currentHeight = document.body.clientHeight
const realWidth = containerPort.value.width || viewport.value.width
const realHeight = containerPort.value.height || viewport.value.height
// const widthScale = currentWidth / realWidth
// const heightScale = currentHeight / realHeight
if (containerTarget.value) {
containerTarget.value.style.transform = `scale(${currentWidth / realWidth},${currentHeight / realHeight})`
}
}
</script>
<style scoped>
.container {
position: fixed;
top: 0;
left: 0;
overflow: hidden;
transform-origin: left top;
z-index: 999;
}
</style>
3.引入并使用Container组件
option可以指定宽高比
<template>
<Container :options="{ width: 1920, height: 1080 }">
<div>1</div>
</Container>
</template>
<script setup>
import Container from "@/components/container/index.vue"
</script>
<style scoped>
</style>
4.运行效果