美文网首页大屏
vue版本大屏适配组件

vue版本大屏适配组件

作者: 小俊的世界 | 来源:发表于2022-04-15 21:37 被阅读0次

    起因

    最近开发一个信息管理大屏,屏幕尽寸为2560*1080。
    公司内部的有一些项目上对于大屏是直接定死宽度与高度的,这样在开发时,特别是针对这种特大屏,开发人员很难有整体感受。还有一些是进行了适配的处理,但是是针对于当前的项目进行处理的,并且还没有沉淀出工具可以直接复用。

    适配方案

    利用rem进行布局,最著名的就是淘宝的flexable布局。其核心的原理,就是更改更路径下的 font-size,然后,使用的大小全部从px转换为rem,这需要开发人员进行计算,还可以利用css预处理语言中(项目中使用是scss)的高级功能来实现px2rem。但是整体上来说,还是比较复杂的。

    利用百分比,同样这里开发人员也就是需要进行计算的。

    第三种,利用scale进行缩放处理,也就是本组件所使用的方式。

    注意点

    但是对于中间的区域 ,我们通过是使用加载模型,或者地图相关,此处是不能直接使用scale进行区域缩放的,只处理对宽高进行比例计算,它会自动适配。

    核心技术

    scale

    scale是css3中的属性。一般情况下默认缩放中心点,是图形的中心点,但是在使用translate(-50%,-50%)时,需要将默认缩放中心点变为左上角。

    transform:scale(0.5);
    transform-origin:0 0;
    

    css变量

    css变量是可以由开发者进行自定义,必须要以 -- 开头的,然后利用var()函数在其它css属性中使用,它是对大写小敏感的。

    element{
        --color:red;
    }
    
    div{
        color:var(--color)
    }
    

    如何利用js来操作css变量?
    方式一:直接内联到元素中
    document.body.style.setProperty(--color, 'red')

    方式二:创建style 再来插入

    const styleEl = documente.createElement('style')
    styleEl.innerHTML = `
     :root{
         --color:red; 
     }
    `
    document.body.append(styleEl)
    

    水平垂直居中

    因为涉及到多层 所以可以采用定位的方式

    <div class="parent">
    <div class="son"></div>
    </div>
    <style>
    .parent{
        position:relative;
        width:100%;
        height:100vh;
    }
    .son{
        position:absolute;
        left:50%;
        top:50%;
        transform:translate(-50%,-50%);
    }
    </style>
    

    组件使用

        class="ssfc-screen"
        prop-name="ssfc-screen-scale"
        adpter-class="adpter-area"
        :width="width"
        :height="height"
        :resize-listenter="resizeListenter"
      >
        <cesium-main slot="main"></cesium-main>
        <div>
          <div v-show="ifShow">
            <top-bar></top-bar>
            <!--    左侧面板-->
            <left></left>
            <!--    右侧侧面板-->
            <right></right>
            <!--    底部 -->
            <bottom></bottom>
            <!-- ./staticData/imgs/allScreen.png -->
            <!-- 模型上其余部分 -->
            <remainingAreas class="reset-events"></remainingAreas>
          </div>
    
          <img
            :src="picUrl"
            alt=""
            class="reset-events"
            :class="ifShow ? 'imgFull' : 'imgPart'"
            @click="
              ifShow = !ifShow
              getFullScreen()
            "
          />
        </div>
      </AdpterScreen>
    
    slot="main" 主区域不进行缩放  其它区域进行缩放
    propName  css变量名 注意不需要加 --
    adpterClass 适配类  可供弹框等使用
    width height 设计稿宽高
    resizeListenter risize事件监听里面回调参数为当前缩放值 
    

    源码

    <!--
     * @Description
     * @Autor 朱俊
     * @Date 2022-04-13 17:19:05
     * @LastEditors: wangs
     * @LastEditTime: 2022-04-14 18:03:24
    -->
    <template>
      <div class="big-screen-wrapper" ref="containerRef">
        <div class="main-wrapper" ref="mainRef">
          <slot name="main"></slot>
        </div>
        <div class="layer-wrapper" ref="layerRef">
          <slot />
        </div>
      </div>
    </template>
    <script>
    import ScaleLayout from './scaleLayout'
    export default {
      props: {
        propName: {
          type: String,
          default: 'scale' + new Date().getTime()
        },
        width: {
          type: Number,
          default: 1920
        },
        height: {
          type: Number,
          default: 1080
        },
        adpterClass: {
          type: String,
          default: 'apter-area'
        },
        resizeListenter: {
          type: Function,
          default: () => {
            return () => {}
          }
        }
      },
      data() {
        return {}
      },
      mounted() {
        this.$nextTick(() => {
          const container = document.body
          const mainEl = this.$refs['mainRef']
          const layerEl = this.$refs['layerRef']
          new ScaleLayout({
            context: this,
            propName: this.propName,
            width: this.width,
            height: this.height,
            container,
            mainEl,
            layerEl,
            adpterClass: this.adpterClass,
            resizeListenter: this.resizeListenter
          })
        })
      }
    }
    </script>
    
    <style lang="scss" scoped>
    .big-screen-wrapper {
      width: 100%;
      height: 100vh;
      position: relative;
      background: #000;
      overflow: hidden;
      .main-wrapper {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
      }
    
      .layer-wrapper {
        position: absolute;
        left: 50%;
        top: 50%;
        overflow: hidden;
    
        pointer-events: none; // 图层事件穿透
    
        * {
          // 其它事件恢复
          pointer-events: auto;
        }
      }
    }
    </style>
    
    
    /*
     * @Description
     * @Autor 朱俊
     * @Date 2022-04-13 17:19:27
     * @LastEditors 朱俊
     * @LastEditTime 2022-04-14 23:05:19
     */
    // 防抖
    function debounce(fn, t) {
      const delay = t || 500
      let timer
      return (...args) => {
        if (timer) {
          clearTimeout(timer)
        }
        const context = this
        timer = setTimeout(() => {
          timer = null
          fn.apply(context, args)
        }, delay)
      }
    }
    
    class ScaleLayout {
      constructor({
        context,
        width,
        height,
        container,
        propName,
        mainEl,
        layerEl,
        adpeterClass,
        resizeListenter
      }) {
        this.styleEl = null
        this.scale = 1 // 默认初始缩放1
        this.context = context
        this.width = width // 设计稿宽度
        this.height = height // 设计稿高度
        this.container = container || document.body
        this.propName = propName || 'scale'
        this.mainEl = mainEl
        this.layerEl = layerEl
        this.adpeterClass = adpeterClass || 'adpter-area'
        this.resizeListenter = resizeListenter || (() => {})
        this.dealLayout()
        this.setScale()
        this.resizeListenter(this.scale)
        this.listen()
      }
    
      // 处理布局
      dealLayout() {
        // 处理main区域
        this.mainEl.style = `
          width: calc(${this.width}px * var(--${this.propName}));
          height: calc(${this.height}px * var(--${this.propName}));
        `
        // 处理图层
        this.layerEl.style = `
         width: ${this.width}px;
         height: ${this.height}px;
         transform: scale(var(--${this.propName})) translate(-50%,-50%);
         transform-origin:0 0;
        `
    
        // 动态生成适配类
        this.styleEl = document.createElement('style')
    
        this.styleEl.innerHTML = `
         .${this.adpeterClass} {
          transform: scale(var(--${this.propName}));
          transform-origin:0 0;
         }
        `
        this.container.append(this.styleEl)
      }
    
      // 页面生命周期及浏览器大小监听
      listen() {
        this.onresize = debounce(() => {
          this.setScale()
          this.resizeListenter(this.scale)
        }, 500)
        window.addEventListener('resize', this.onresize)
    
        this.context.$on('hook:beforeDestroy', () => {
          window.removeEventListener('resize', this.onresize)
          this.styleEl && this.container.removeChild(this.styleEl)
        })
      }
    
      // 设置缩放
      dealScale() {
        const ws = window.innerWidth / this.width
        const hs = window.innerHeight / this.height
        return ws < hs ? ws : hs
      }
      //  获取scale值
      getScale() {
        return this.scale
      }
      // 设置scale
      setScale() {
        this.scale = this.dealScale()
        this.container.style.setProperty(`--${this.propName}`, this.scale)
      }
    }
    
    export default ScaleLayout
    

    相关文章

      网友评论

        本文标题:vue版本大屏适配组件

        本文链接:https://www.haomeiwen.com/subject/ulnqertx.html