Newer
Older
safe_production_front / src / views / bigScreen / components / autoScalContainer.vue
dutingting on 27 Dec 4 KB gm部署版本
<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>