一、大屏适配方案对比

常见的大屏适配方案有3种,对比如下:

方案实现原理优点缺点
scale通过 scale 属性,根据屏幕大小,对图表进行整体的等比缩放1.代码量少,适配简单;;2.一次处理后不需要在各个图表中再去单独适配1.当大屏跟 ui 稿的比例不一样时,会出现周边留白情况2.当缩放比例过大时候,字体和图片会有一点点失真.;3.当缩放比例过大时候,事件热区会偏移。(如地图上的点击事件)
vw,vh按照设计稿的尺寸,将px按比例计算转为vw和vh1.可以动态计算图表的宽高,字体等,灵活性较高; 2.当屏幕比例跟 ui 稿不一致时,不会出现两边留白情况1.需要编写公共转换函数,为每个图表都单独做字体、间距、位移的适配,比较麻烦
rem + vw vh1.获得 rem 的基准值2.动态的计算html根元素的font-size3.图表中通过 vw vh 动态计算字体、间距、位移等1.灵活性高、兼容性好、适应性强1.需要进行许多计算,可能存在误差问题,且代码复杂度较高

本篇分享的是 scale 的方法,适用场景:设备比例固定;无地图点击事件
没有哪种方案是最好或者哪种方案不好,方案选择,需要根据实际开发需求及场景来确定。

二、效果

http://192.168.2.46:3006/#/screen (未部署,本地服务地址)

三、原理思路

1.如何缩放:

通过css函数 :scale(缩放倍数)
https://codepen.io/csscodelct/pen/rNRzRov

通过以上demo我们了解到,实现缩放,我们只需要获取 缩放比例 即可。

2.获取缩放比例

  • 确定设计稿尺寸,默认 1920 x 1080
  • 分别计算浏览器和设计图宽高比
  • 如果浏览器的宽高比大于设计稿的宽高比,就取浏览器高度:设计稿高度,否则取浏览器宽度:设计稿宽度

浏览器的宽高比大于设计稿的宽高比
image.png

浏览器的宽高比小于设计稿的宽高比
image.png

根据以上图片可知,我们无论怎么缩放,都不能超出可视范围(即浏览器区域)

3.使用防抖优化性能

防抖使用场景: 高频率触发的事件,在指定的单位时间内,只响应最后一次,如果在指定的时间内再次触发,则重新计算时间。如:按钮点击、输入框监听

function debounce(callback, delay) {
    let timerId
    return function (event) {
        // 如果上次事件还没有真正处理, 清除
        if (timerId) {
            clearTimeout(timerId)
        }

        // 发事件发生指定事件后才调用处理事件的回调函数
        // 启动定时器, 只是准备真正处理
        timerId = setTimeout(() => {
            // 正在处理事件
            callback.call(null, event)
            // 删除准备处理的标记
            timerId = null
        }, delay)
    }
}

四、代码实现

封装一个公共方法 src/utils/useResize.ts

import { ref, onMounted, onBeforeUnmount } from "vue";

// 默认适配宽高
export const width = 1920;
export const height = 1080;

type ResizeType = {
    w?: number;
    h?: number;
    delay?: number;
};

export const useResize = (options: ResizeType = {}) => {
    const { w = width, h = height, delay = 100 } = options;
    // 缩放元素
    const screenRef = ref();
    const scale = ref(1);
    function resize() {
        // 浏览器宽高
        const clientWidth = document.body.clientWidth;
        const clientHeight = document.body.clientHeight;

        // 计算宽高缩放比例
        const scaleW = clientWidth / w;
        const scaleH = clientHeight / h;

        if (clientWidth / clientHeight > w / h) {
            // 如果浏览器的宽高比大于设计稿的宽高比,就取浏览器高度和设计稿高度之比
            scale.value = scaleH;
        } else {
            // 如果浏览器的宽高比小于设计稿的宽高比,就取浏览器宽度和设计稿宽度之比
            scale.value = scaleW;
        }
        
        screenRef.value.style.transform = "scale(" + scale.value + ")";
    }

    const resizeDelay = debounce(resize, delay);
    onMounted(() => {
        if (screenRef.value) {
            resize();
            window.addEventListener("resize", resizeDelay);
        }
    });

    onBeforeUnmount(() => {
        window.removeEventListener("resize", resizeDelay);
    });

    return {
        scale,
        screenRef,
    };
};

/*
用来返回防抖函数的工具函数
*/
function debounce(callback, delay) {
    let timerId;
    return function (event) {
        // 如果上次事件还没有真正处理, 清除
        if (timerId) {
            clearTimeout(timerId);
        }

        // 发事件发生指定事件后才调用处理事件的回调函数
        // 启动定时器, 只是准备真正处理
        timerId = setTimeout(() => {
            // 正在处理事件
            callback.call(null, event);
            // 删除准备处理的标记
            timerId = null;
        }, delay);
    };
}

页面中使用

<template>
    <div ref="screenRef"></div>
</template>

<script setup lang='ts'>
import { useResize } from '@/utils/useResize'
const { screenRef } = useResize()
</script>

ok了
参考:https://juejin.cn/post/7248242431659966522#heading-1


Hard heart
1 声望0 粉丝