大屏适配方案

1,849 阅读3分钟

transform: scale(x, y)

通过监测window 的resize 事件,在resize 事件中,实时的获取浏览器视口的宽和高,用视口的宽、高度与UI 设计稿的宽、高度进行比较,得出实际缩放值。

 // 实现方案:保持和设计稿比例
 // 大屏中有图片背景、包含各种点击事件,但没有地图组件
 <template>
     <div id="screen"></div>
 </template>
 <script>
     export default {
         mounted() {
             this.setScale()
             window.onresize = this.debounce(this.setScale, 10)
         },
         methods: {
             debounce: (fn, t) => {
                 const delay = t || 500
                 let timer
                 return function() {
                     const args = arguments
                     if (timer) {
                         clearTimeout(timer)
                     }
                     const context = this
                     timer = setTimeout(() => {
                         timer = null
                         fn.apply(context, args)
                     }, delay)
                 }
             },
             // 获取放大缩小比例
             getScale() {
                 const w = window.innerWidth / 1920
                 const h = window.innerHeight / 1000
                 return [w, h]
             },
             // 设置比例
             setScale() {
                 const bigScreen = document.querySelector('#screen')
                 bigScreen.style.transform = `scale(${this.getScale()[0]},${this.getScale()[1]})`
                 bigScreen.style.transformOrigin = '(0, 0)'
             }
         }
     }
 </script>
 <style lang="scss" scoped>
     #screen {
         z-index: 999;
         top: 0;
         right: 0;
         left: 0;
         position: fixed;
         display: block;
         height: 1080px;
         width: 1920px;
         transform-origin: 0 0;
         background: url(../../../assets/base/img/screen_base.png) no-repeat;
         background-size: 100% 100%;
     }
 </style>
// 类似方案:思路一样
<template>
    <div class="Scale" ref="Scale" :style="{
      width: width + 'px',
      height: height + 'px',
    }">
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: 'scale-container',
    props: {
        width: {
            type: Number,
            default: () => 1920
        },
        height: {
            type: Number,
            default: () => 1080
        }
    },
    data () {
        return {}
    },
    mounted () {
        this.setScale()
        window.addEventListener('resize', this.debounce(this.setScale))
    },
    methods: {
        getScale () {
            const { width, height } = this
            const wh = window.innerHeight / height
            const ww = window.innerWidth / width
            return { scaleX: ww, scaleY: wh }
        },
        setScale () {
            const { scaleY, scaleX } = this.getScale()
            if (this.$refs.Scale) {
                this.$refs.Scale.style.setProperty('--scaleX', scaleX)
                this.$refs.Scale.style.setProperty('--scaleY', scaleY)
            }
        },
        debounce (fn, delay) {
            const delays = delay || 200
            let timer
            return function () {
                const th = this
                const args = arguments
                if (timer) {
                    clearTimeout(timer)
                }
                timer = setTimeout(function () {
                    timer = null
                    fn.apply(th, args)
                }, delays)
            }
        }
    }
}
</script>

<style lang="less">
.Scale {
    position: absolute;
    transform: scale(var(--scaleX), var(--scaleY)) translate(-50%, -50%);
    display: flex;
    flex-direction: column;
    transform-origin: 0 0;
    left: 50%;
    top: 50%;
    transition: 0.3s;
    z-index: 999;
}
</style>

【总结】

优势

  • 兼容性更好
  • 不会引起重排(回流),只会发生重绘

劣势

scale 缩放有两种方式,一种是缩放不保持U I设计稿比例缩放,另一种是保持UI 设计稿比例缩放

不保持UI 设计稿比例缩放

  • 对于同一比例的分辨率适配,对于不同比例的分辨率,scale 缩放会直接导致页面有压缩。
  • 对于antd 组件库中的Select 的弹出层位置也会出现很大偏差
  • 如果地图是高德地图或者其他GIS 地图的话,会导致鼠标点击地图的点有位置偏差,此种情况下,采用对地图再次应用scale,地图的scale 值与body 身上的scale 值相乘等于1即可

保持UI 设计稿比例缩放

  • 如果地图是高德地图或者其他GIS 地图的话,会导致鼠标点击地图的点有位置偏差。(目前没找到完美解决方案)
  • 对于antd 组件库中的Select 的弹出层位置也会出现很大偏差

v-scale-screen - transform

vue大屏自适应终极解决方案

v-scale-screen使用css属性transform 实现缩放效果,进行等比例计算,达到等比例缩放的效果,同时也支持铺满全屏,宽度等比,高度等比,等自适应方案

npm install v-scale-screen -save

vue2 中使用插件导入,在main.js 中引入

// main.js
import VScaleScreen from 'v-scale-screen'
Vue.use(VScaleScreen)

vue3 以组件导入

<v-scale-screen width="1920" height="1080">
    <div>
        <v-chart>....</v-chart>
        <v-chart>....</v-chart>
        <v-chart>....</v-chart>
        <v-chart>....</v-chart>
        <v-chart>....</v-chart>
    </div>
</v-scale-screen>
<script>
import VScaleScreen from 'v-scale-screen'
export default {
    components:{
        VScaleScreen
    }
}
</script> 

【分析】

  • 上下左右会有留白
  • 地图组件,坐标位置会有问题 => 使用ifame引入组件

rem 适配

1、安装 postcss-pxtorem 插件,在webpack 打包的时候,将css 中的px 自动转成rem。

npm install postcss postcss-loader postcss-pxtorem --save

2、项目根目录下创建postcss.config.js 文件

 module.exports = {
     plugins: [
         [
             'postcss-preset-env',
             {
                 // autoprefixer: {
                 //   grid: true,
                 // },
             },
         ],
         [             'postcss-pxtorem',             {                 rootValue: 100,                 propList: ['*'],
             },
         ],
     ],
 }

3、修改webpack 配置,编译sass/less 的时候,新增postcss-loader,注意:postcss-loader 需要配置在css-loader 之前执行

 {
     loader: require.resolve('postcss-loader'),
 }

4、动态修改html 标签fontSize 的大小

 // 封装一个根据屏幕尺寸自动改变html 的font-size 大小的函数
 const htmlDOM = document.getElementsByTagName('html')[0]
 const designWidth = 1920 // 设计稿的宽度  --- 已知
 const designHtmlFontSize = 100 // 设计稿上面的html font-size的大小  --- 已知
 let actualHtmlFontSize // 实际html font-size的大小 --- 未知
 function setRootFontSize() {
   const viewportWidth = document.documentElement.clientWidth || document.body.clientWidth // 实际浏览器视口的宽度 ---- 已知
   actualHtmlFontSize = viewportWidth * designHtmlFontSize / designWidth // 3个已知条件,1个未知条件
   htmlDOM.style.fontSize = actualHtmlFontSize + 'px'
 }
 ​
 setRootFontSize()
 window.addEventListener('resize', setRootFontSize)

【总结】

优势: 自适应流畅,实时根据浏览器视口的宽度变化而适应。开发者书写样式的时候,无需考虑单位转换问题。

劣势:会受到浏览器fontSize 最小为12px 的影响,同时也会受到windows 系统自带的缩放影响,根据实际使用来看rem 布局在web端只适配不同分辨率但是同一宽高比,并且windows 系统缩放为100% 的情况。

所以在web端 采用rem 布局不太合适,移动端采用rem 布局问题是不大的。

zoom 适配 - zoom

export const setBodyZoom = () => {
    const t = window.screen.width
    let zoomNum = t / 1920 * 10
    zoomNum = Math.round(zoomNum) / 10
    document.body.style.zoom = zoomNum
}

【参考资料】

一次大屏可视化项目自适应可行性方案探究

vue大屏自适应终极解决方案