如何自己做一个大屏自适应容器组件,终极解决方案

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.运行效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值