<!-- 
 | 
 * @Author: wei 
 | 
 * @description: 大屏自适应容器组件 
 | 
 * @LastEditTime: 2022-09-09 16:42:40 
 | 
--> 
 | 
<template> 
 | 
  <!-- <section class="screen-box" :style="boxStyle"> --> 
 | 
  <div class="screen-wrapper" ref="screenWrapper" :style="wrapperStyle"> 
 | 
    <slot></slot> 
 | 
  </div> 
 | 
  <!-- </section> --> 
 | 
</template> 
 | 
<script> 
 | 
/** 
 | 
 * 防抖函数 
 | 
 * @param {T} fn 
 | 
 * @param {number} delay 
 | 
 * @return 
 | 
 */ 
 | 
function debounce(fn, delay) { 
 | 
  let timer = null; 
 | 
  return function (...args) { 
 | 
    timer = setTimeout( 
 | 
      () => { 
 | 
        typeof fn === "function" && fn.apply(null, args); 
 | 
        clearTimeout(timer); 
 | 
      }, 
 | 
      delay > 0 ? delay : 100 
 | 
    ); 
 | 
  }; 
 | 
} 
 | 
  
 | 
export default { 
 | 
  name: "VScaleScreen", 
 | 
  props: { 
 | 
    width: { 
 | 
      type: [String, Number], 
 | 
      default: 1920, 
 | 
    }, 
 | 
    height: { 
 | 
      type: [String, Number], 
 | 
      default: 1080, 
 | 
    }, 
 | 
    fullScreen: { 
 | 
      type: Boolean, 
 | 
      default: false, 
 | 
    }, 
 | 
    autoScale: { 
 | 
      type: [Object, Boolean], 
 | 
      default: true, 
 | 
    }, 
 | 
    selfAdaption: { 
 | 
      type: Boolean, 
 | 
      default: true, 
 | 
    }, 
 | 
    delay: { 
 | 
      type: Number, 
 | 
      default: 500, 
 | 
    }, 
 | 
    boxStyle: { 
 | 
      type: Object, 
 | 
      default: () => ({}), 
 | 
    }, 
 | 
    wrapperStyle: { 
 | 
      type: Object, 
 | 
      default: () => ({}), 
 | 
    }, 
 | 
  }, 
 | 
  data() { 
 | 
    return { 
 | 
      currentWidth: 0, 
 | 
      currentHeight: 0, 
 | 
      originalWidth: 0, 
 | 
      originalHeight: 0, 
 | 
      onResize: null, 
 | 
      observer: null, 
 | 
    }; 
 | 
  }, 
 | 
  watch: { 
 | 
    selfAdaption(val) { 
 | 
      if (val) { 
 | 
        this.resize(); 
 | 
        this.addListener(); 
 | 
      } else { 
 | 
        this.clearListener(); 
 | 
        this.clearStyle(); 
 | 
      } 
 | 
    }, 
 | 
  }, 
 | 
  computed: { 
 | 
    screenWrapper() { 
 | 
      return this.$refs["screenWrapper"]; 
 | 
    }, 
 | 
  }, 
 | 
  methods: { 
 | 
    initSize() { 
 | 
      return new Promise((resolve, reject) => { 
 | 
        // console.log("初始化样式"); 
 | 
        //给父元素设置 overflow:hidden 
 | 
        this.screenWrapper.parentNode.style.overflow = "hidden"; 
 | 
        this.screenWrapper.parentNode.scrollLeft = 0; 
 | 
        this.screenWrapper.parentNode.scrollTop = 0; 
 | 
  
 | 
        this.$nextTick(() => { 
 | 
          // region 获取大屏真实尺寸 
 | 
          if (this.width && this.height) { 
 | 
            this.currentWidth = this.width; 
 | 
            this.currentHeight = this.height; 
 | 
          } else { 
 | 
            this.currentWidth = this.screenWrapper.clientWidth; 
 | 
            this.currentHeight = this.screenWrapper.clientHeight; 
 | 
          } 
 | 
          // endregion 
 | 
          // region 获取画布尺寸 
 | 
          if (!this.originalHeight || !this.originalWidth) { 
 | 
            this.originalWidth = window.screen.width; 
 | 
            this.originalHeight = window.screen.height; 
 | 
          } 
 | 
          // endregion 
 | 
          resolve(); 
 | 
        }); 
 | 
      }); 
 | 
    }, 
 | 
    updateSize() { 
 | 
      if (this.currentWidth && this.currentHeight) { 
 | 
        this.screenWrapper.style.width = `${this.currentWidth}px`; 
 | 
        this.screenWrapper.style.height = `${this.currentHeight}px`; 
 | 
      } else { 
 | 
        this.screenWrapper.style.width = `${this.originalWidth}px`; 
 | 
        this.screenWrapper.style.height = `${this.originalHeight}px`; 
 | 
      } 
 | 
    }, 
 | 
    handleAutoScale(scale) { 
 | 
      if (!this.autoScale) return; 
 | 
      const screenWrapper = this.screenWrapper; 
 | 
      const domWidth = screenWrapper.clientWidth; 
 | 
      const domHeight = screenWrapper.clientHeight; 
 | 
      const currentWidth = document.body.clientWidth; 
 | 
      const currentHeight = document.body.clientHeight; 
 | 
      screenWrapper.style.transform = `scale(${scale},${scale}) `; 
 | 
      let mx = Math.max((currentWidth - domWidth * scale) / 2, 0); 
 | 
      let my = Math.max((currentHeight - domHeight * scale) / 2, 0); 
 | 
      if (typeof this.autoScale === "object") { 
 | 
        // @ts-ignore 
 | 
        !this.autoScale.x && (mx = 0); 
 | 
        // @ts-ignore 
 | 
        !this.autoScale.y && (my = 0); 
 | 
      } 
 | 
      // console.log({ 
 | 
      //   mx, 
 | 
      //   my, 
 | 
      //   currentWidth, 
 | 
      //   currentHeight, 
 | 
      //   domWidth, 
 | 
      //   domHeight, 
 | 
      //   scale, 
 | 
      // }); 
 | 
      this.screenWrapper.style.margin = `${my}px ${mx}px`; 
 | 
    }, 
 | 
    updateScale() { 
 | 
      const screenWrapper = this.screenWrapper; 
 | 
      // 获取真实视口尺寸 
 | 
      const currentWidth = document.body.clientWidth; 
 | 
      const currentHeight = document.body.clientHeight; 
 | 
      // 获取大屏最终的宽高onResize 
 | 
      const realWidth = this.currentWidth || this.originalWidth; 
 | 
      const realHeight = this.currentHeight || this.originalHeight; 
 | 
      // 计算缩放比例 
 | 
      const widthScale = currentWidth / realWidth; 
 | 
      const heightScale = currentHeight / realHeight; 
 | 
      // console.log({currentWidth, currentHeight,realWidth,realHeight}); 
 | 
  
 | 
      // 若要铺满全屏,则按照各自比例缩放 
 | 
      if (this.fullScreen) { 
 | 
        screenWrapper.style.transform = `scale(${widthScale},${heightScale})`; 
 | 
        return false; 
 | 
      } 
 | 
      // 按照宽高最小比例进行缩放 
 | 
      const scale = Math.min(widthScale, heightScale); 
 | 
      this.handleAutoScale(scale); 
 | 
    }, 
 | 
    initMutationObserver() { 
 | 
      const screenWrapper = this.screenWrapper; 
 | 
      const observer = (this.observer = new MutationObserver(() => { 
 | 
        this.onResize(); 
 | 
      })); 
 | 
  
 | 
      observer.observe(screenWrapper, { 
 | 
        attributes: true, 
 | 
        attributeFilter: ["style"], 
 | 
        attributeOldValue: true, 
 | 
      }); 
 | 
    }, 
 | 
    clearListener() { 
 | 
      window.removeEventListener("resize", this.onResize); 
 | 
    }, 
 | 
    addListener() { 
 | 
      window.addEventListener("resize", this.onResize); 
 | 
    }, 
 | 
    clearStyle() { 
 | 
      // console.log("清除"); 
 | 
      const screenWrapper = this.screenWrapper; 
 | 
      screenWrapper.parentNode.style.overflow = "auto"; 
 | 
  
 | 
      screenWrapper.style = ""; 
 | 
    }, 
 | 
    async resize() { 
 | 
      if (!this.selfAdaption) { 
 | 
        return; 
 | 
      } 
 | 
      await this.initSize(); 
 | 
      this.updateSize(); 
 | 
      this.updateScale(); 
 | 
    }, 
 | 
  }, 
 | 
  mounted() { 
 | 
    this.onResize = debounce(() => { 
 | 
      this.resize(); 
 | 
    }, this.delay); 
 | 
    this.$nextTick(() => { 
 | 
      if (this.selfAdaption) { 
 | 
        this.resize(); 
 | 
        this.addListener(); 
 | 
      } 
 | 
    }); 
 | 
  }, 
 | 
  beforeDestroy() { 
 | 
    this.clearListener(); 
 | 
    // this.observer.disconnect() 
 | 
  }, 
 | 
}; 
 | 
// 
 | 
</script> 
 | 
  
 | 
<style scoped> 
 | 
.screen-box { 
 | 
  overflow: hidden; 
 | 
  background-size: 100% 100%; 
 | 
  background: #000; 
 | 
  width: 100vw; 
 | 
  height: 100vh; 
 | 
} 
 | 
  
 | 
.screen-wrapper { 
 | 
  transition-property: all; 
 | 
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 
 | 
  transition-duration: 500ms; 
 | 
  position: relative; 
 | 
  overflow: hidden; 
 | 
  z-index: 100; 
 | 
  transform-origin: left top; 
 | 
} 
 | 
</style> 
 |