Skip to content

大屏适配

常见的大屏自适应方案

rem + fontSize

利用 rem 是根元素(html)的字体(font-size)大小这个特点,我们把整个页面的 CSS 单位都统一为 rem ,这样我们就只用修改根元素的字体大小,实现页面的整体动态伸缩。

fontSize 设置思路:

  1. html { font-size: 100px; },把 100px 当做因子
  2. 如果要设置 1200px 宽,那么换算下来就是 { width: 12rem }
  3. 这样也不会受到浏览器默认字体的影响(对比设置 html 字体大小为 62.5% )

vw、vh

这种方案的原理就是把屏幕宽高等分成 100 份, 得到每份的 px 大小, 然后再用设计稿像素大小除以 每份的 px 大小,就得到了对应的 vw/vh 值

shell
假设设计稿尺寸为 1920*1080

即:
网页宽度=1920px
网页高度=1080px

我们都知道
网页宽度=100vw
网页宽度=100vh

所以,在 1920px*1080px 的屏幕分辨率下

1920px = 100vw

1080px = 100vh

这样一来,以一个宽 300px 200px div 来说,其所占的宽高,以 vw vh 为单位,计算方式如下:

vwDiv = 300px / (1920px / 100vw)
vhDiv = 200px / (1080px / 100vh)

所以,就在 1920*1080 的屏幕分辨率下,计算出了单个 div 的宽高

当屏幕放大或者缩小时,div 还是以 vw vh 作为宽高的,就会自动适应不同分辨率的屏幕

scale (推荐方案)

利用 css3 的 transform: scale(X) 来根据宽高比计算当前大屏容器需要设置的比例 scale:

ts
import { ref, onMounted, onUnmounted, watch, Ref } from 'vue';

interface IOptions {
  el: Ref<HTMLElement | null> | HTMLElement | string;
  dW: number;
  dH: number;
  parentEl?: Ref<HTMLElement | null> | HTMLElement | string;
  isRatio?: boolean;
}

export function useBigScreen({
  el,
  dW,
  dH,
  parentEl = document.body,
  isRatio = true
}: IOptions) {
  const scale = ref(1);

  const getElement = (element: HTMLElement | Ref<HTMLElement | null> | string | null): HTMLElement | null => {
    if (element instanceof HTMLElement) return element;
    if (typeof element === 'string') return document.querySelector(element);
    return element?.value ?? null;
  }

  const setTargetElementStyle = (element: HTMLElement) => {
    element.style.position = 'absolute';
    element.style.left = '50%';
    element.style.top = '50%';
    element.style.width = +dW + 'px';
    element.style.height = +dH + 'px';
  }

  const setScale = () => {
    const targetEl = getElement(el);
    const containerEl = parentEl ? getElement(parentEl) : document.body;
    
    if (!targetEl || !containerEl) {
      console.log('目标元素或者目标元素的父元素没有找到.');
      return;
    }

    setTargetElementStyle(targetEl);

    const containerWidth = containerEl.clientWidth;
    const containerHeight = containerEl.clientHeight;

    let scaleX = containerWidth / dW;
    let scaleY = containerHeight / dH;

    if (isRatio) {
      // 保持宽高比,按最长边进行缩放
      const scaleValue = Math.min(scaleX, scaleY);
      targetEl.style.transform = `translate(-50%, -50%) scale(${scaleValue})`;
    } else {
      // 不保持宽高比,分别按宽高拉伸
      targetEl.style.transform = `translate(-50%, -50%) scale(${scaleX}, ${scaleY})`;
    }
  }

  const onResize = () => {
    setScale();
  }

  onMounted(() => {
    setScale();
    window.addEventListener('resize', onResize);
  });

  onUnmounted(() => {
    window.removeEventListener('resize', onResize);
  });

  // 如果 el 或 parentEl 动态改变时重新计算
  watch([() => el, () => parentEl], () => {
    setScale();
  });

  return { scale };
}

使用:

vue
<template>
  <div ref="myScreen" class="monitor-screen"></div>
</template>

<script setup lang="ts">
import { onMounted, reactive, ref } from "vue";

import { useBigScreen } from "../../hooks/useBigScreen";
const myScreen = ref<HTMLElement | null>(null);
useBigScreen({
  el: myScreen, // 目标 DOM
  dW: 1920, // 设计稿宽度
  dH: 1080, // 设计稿高度
  parentEl: undefined, // 默认基于 body 进行全屏适配
  isRatio: true, // 保持设计稿宽高比
});
</script>

此方案的示例 DEMO 地址

http://124.221.90.67/playground/big-screen/