Estratégias de Dimensionamento Adaptativo para Painéis de Visualização de Dados

Dimensionamento via Unidades de Viewport (vw/vh)

A conversão direta de valores fixos para unidades relativas à janela de visualização garante que os elementos ocupem percentuais consistentes do espaço disponível, independentemente da resolução. A lógica baseia-se na proporção entre o layout de referência e o viewport atual.

Para um projeto definido em 1920 x 1080, a conversão matemática segue:

// Cálculo base
largura_em_vw = (valor_px / 1920) * 100vw
altura_em_vh = (valor_px / 1080) * 100vh

Helpers em Pré-processadores CSS

Configuração para SCSS:

@use "sass:math";

$BASE_W: 1920;
$BASE_H: 1080;

@function calc-vw($val) {
  @return math.div($val, $BASE_W) * 100vw;
}

@function calc-vh($val) {
  @return math.div($val, $BASE_H) * 100vh;
}

Configuração para Less:

@base-w: 1920;
@base-h: 1080;

.to-vw(@prop, @val) {
  @{prop}: (@val / @base-w) * 100vw;
}

.to-vh(@prop, @val) {
  @{prop}: (@val / @base-h) * 100vh;
}

Para aplicar globalmente no ecossistema Vue, injete os arquvios de utilitários no processador de CSS via configuração de compilação (vue.config.js):

module.exports = {
  css: {
    loaderOptions: {
      sass: {
        additionalData: `@import "@/styles/helpers.scss";`
      },
      less: {
        additionalData: `@import "@/styles/helpers.less";`
      }
    }
  }
};

Alternativa via JavaScript

Caso a manipulação deva ocorer em runtime ou em estilos inline, a conversão pode ser centralizada:

const REF_WIDTH = 1920;
const REF_HEIGHT = 1080;

export const toVw = (px) => `${(px * 100) / REF_WIDTH}vw`;
export const toVh = (px) => `${(px * 100) / REF_HEIGHT}vh`;

Ajuste Automático de Componentes Gráficos

Bibliotecas como o ECharts não recalculam automaticamente o conteúdo quando apenas a janela redimensiona, exigindo monitoramento direto do container. Uma diretiva Vue isola essa responsabilidade:

import ERD from 'element-resize-detector';
import echarts from 'echarts';
import Vue from 'vue';

const INSTANCE_KEY = '_chart_erd_handler';

Vue.directive('auto-resize-chart', {
  bind(el, binding) {
    const handler = binding.value || (() => {
      const chart = echarts.getInstanceByDom(el);
      chart && chart.resize();
    });
    el[INSTANCE_KEY] = handler;
    ERD({ strategy: 'scroll' }).listenTo(el, el[INSTANCE_KEY]);
  },
  unbind(el) {
    if (el[INSTANCE_KEY]) {
      ERD().removeListener(el, el[INSTANCE_KEY]);
      delete el[INSTANCE_KEY];
    }
  }
});

Para métricas internas do gráfico (fontes, sombras, margens), que aceitam apenas números, crie uma função de adaptação:

export const adaptMetric = (rawVal, refW = 1920) => {
  const currentW = window.innerWidth || document.documentElement.clientWidth;
  if (!currentW) return rawVal;
  return parseFloat(((rawVal * currentW) / refW).toFixed(2));
};

// Uso em opções do ECharts
const config = {
  grid: {
    top: adaptMetric(24),
    bottom: adaptMetric(12)
  }
};

Escala Global via CSS Transform

Esta abordagem mantém a proporção exata do layout original. O contêiner é centralizado e recebe um fator de escala calculado pela comparação das proporções (aspect ratio) entre a tela atual e o mockup.

.viewport-container {
  width: 100%;
  height: 100%;
  position: relative;
}

.scalable-content {
  position: absolute;
  top: 50%;
  left: 50%;
  transform-origin: top left;
  /* Dimensões fixas do design */
  width: 1920px;
  height: 960px;
}

O cálculo da transformação ocorre em montagem e redimensionamento:

const REF_W = 1920;
const REF_H = 960;

function applyGlobalScale() {
  const viewW = document.documentElement.clientWidth;
  const viewH = document.documentElement.clientHeight;
  
  const refRatio = REF_W / REF_H;
  const viewRatio = viewW / viewH;
  
  // Determina o limitador (largura ou altura) para manter proporção sem corte
  const factor = viewRatio < refRatio 
    ? viewW / REF_W 
    : viewH / REF_H;

  const target = document.getElementById('canvas-root');
  target.style.transform = `scale(${factor}) translate(-50%, -50%)`;
}

window.addEventListener('resize', applyGlobalScale);
// Chamar na montagem do componente

Essa técnica gera barras pretas (letterbox/pillarbox) quando as proporções divergem, garantindo integridade visual absoluta. Para implementação acelerada, bibliotecas como v-scale-screen encapsulam essa matemática e o posicionamento.

Unidade rem com Recálculo Dinâmico da Raiz

A unidade rem depende do valor de font-size no elemento raiz (html). Ao manipular esse valor dinamicamente conforme a largura da tela, todos os elementos marcados com rem se ajustam proporcionalmente.

A conversão de px para rem pode ser automatizada durante o build com postcss:

// .postcssrc.js
module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue: 192, // 1920px / 10 divisões
      unitPrecision: 3,
      propList: ['*', '!border'],
      mediaQuery: false,
      minPixelValue: 2
    }
  }
};

O script de recalibração da raiz deve rodar no carregamento e nas mudanças de viewport:

(function bootstrapRem(baseRatio = 16 / 9) {
  const html = document.documentElement;
  
  const recalculate = () => {
    const vw = html.clientWidth;
    const vh = html.clientHeight;
    const currentRatio = vw / vh;
    
    // Mantém escala estável até cruzar a proporção alvo
    const scaleFactor = currentRatio > baseRatio ? (baseRatio / currentRatio) : 1;
    const rootSize = (vw * scaleFactor) / 10;
    
    html.style.fontSize = `${rootSize.toFixed(2)}px`;
  };
  
  recalculate();
  window.addEventListener('resize', recalculate);
})();

A inclusão deste módulo no ponto de entrada da aplicação habilita o redimentionamento contínuo. Para elementos de gráficos vetoriais que ignoram CSS, a função de adaptação métrica apresentada na primeira abordagem pode ser reutilizada para ajustar espaçamentos e fontes internas.

Tags: vw-vh-layout transform-scale-css unidade-rem postcss-pxtorem echarts-resize

Publicado em 9-28 15:55