Este artículo detalla un método para adaptar la resolución de aplicaciones Vue a diferentes escalas de navegador, especialmente útil en pantallas de alta densidad de píxeles (HiDPI) y al redimensionar la ventana del navegador. Se enfoca en la implementación mediante un plugin personalizado y la configuración de PostCSS.
Método de Adaptación mediante Plugin Personalizado
Se propone una clase DisplayAdapter para gestionar la adaptación dinámica del diseño.
Configuración Inicial
El plugin requiere la instalación de postcss-px2rem.
npm i postcss-px2rem -S
Archivo devicePixelRatio.js
Este archivo contiene la lógica principal para la adaptación.
class DisplayAdapter {
constructor(options = {}) {
this.options = {
designWidth: 1920, // Ancho de referencia del diseño
baseFontSize: 16, // Tamaño de fuente base en px
maxScale: 2, // Ratio máximo de escalado
useTransform: true, // Usar `transform` para escalar
debounceTime: 100, // Tiempo de debounce para eventos
...options
};
this.lastDPR = window.devicePixelRatio;
this.init();
}
// Detección de sistema (versión mejorada)
_getSystem () {
const agent = navigator.userAgent.toLowerCase();
const isWindows = /windows/.test(agent);
const isHighDPI = window.devicePixelRatio >= 1.25;
return isWindows && isHighDPI;
}
// Función de debounce
_debounce (fn) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), this.options.debounceTime);
};
}
// Cálculo dinámico de REM
_setRem () {
const clientWidth = document.documentElement.clientWidth;
const scale = Math.min(clientWidth / this.options.designWidth, this.options.maxScale);
document.documentElement.style.fontSize =
`${this.options.baseFontSize * scale}px`;
}
// Corrección de escalado por DPI
_correctDPR() {
const currentDPR = window.devicePixelRatio;
if (Math.abs(currentDPR - this.lastDPR) > 0.05) {
this.lastDPR = currentDPR;
if (this.options.useTransform) {
const scale = 1 / currentDPR;
document.body.style.transform = `scale(${scale})`;
document.body.style.transformOrigin = 'top left';
document.body.style.width = `${this.options.designWidth / scale}px`;
} else {
document.body.style.zoom = 1 / currentDPR;
}
}
}
// Combinación de métodos de adaptación
_adaptLayout () {
this._correctDPR();
this._setRem();
}
// Escucha de eventos
_watch () {
const debouncedAdapt = this._debounce(() => this._adaptLayout());
const events = ['resize', 'pageshow', 'orientationchange'];
events.forEach(event => {
window.addEventListener(event, debouncedAdapt);
});
// Escucha de cambios en DPI (solo navegadores modernos)
if (window.matchMedia) {
window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`)
.addEventListener('change', debouncedAdapt);
}
}
// Inyección de estilos base
_injectBaseStyles () {
const style = document.createElement('style');
style.textContent = `
html {
overflow: hidden;
width: 100vw;
height: 100vh;
}
body {
position: relative;
margin: 0;
padding: 0;
overflow: hidden;
transform-origin: top left;
}
`;
document.head.appendChild(style);
}
// Inicialización
init () {
if (this._getSystem()) {
this._injectBaseStyles();
this._adaptLayout();
this._watch();
} else {
// Solo activar adaptación REM en sistemas no Windows
this._setRem();
window.addEventListener('resize', this._debounce(() => this._setRem()));
}
}
}
// Configuración de inicialización
new DisplayAdapter({
designWidth: 1920,
useTransform: true,
maxScale: 1.5
});
export default DisplayAdapter;
Uso en App.vue
Importar e inicializar la clase en el ciclo de vida created del componente principal.
import DevicePixelRatio from "@/utils/devicePixelRatio"
export default {
name: "App",
created () {
new DevicePixelRatio().init()
},
};
Configuración de PostCSS
Se deben ajustar los archivos de configuración de Vue CLI y PostCSS.
Archivo vue.config.js
Configurar postcss-px2rem dentro de las opciones de css.loaderOptions.postcss.
// Importar el plugin de adaptación
const px2rem = require('postcss-px2rem');
// Configurar el tamaño base
const postcss = px2rem({
// Tamaño base `remUnit`, debe coincidir con el definido en `devicePixelRatio.js`
remUnit: 16,
});
module.exports = {
outputDir: "dist",
// publicPath: '/guess/', // Ejemplo de ruta pública
publicPath: "/win/", // Ruta pública configurada
productionSourceMap: false,
css: {
loaderOptions: {
scss: {
additionalData: `@import "./src/assets/styles/module.scss";`,
sassOptions: { outputStyle: "expanded" },
},
postcss: {
plugins: [
postcss,
],
},
},
},
};
Archivo postcss.config.js (en la raíz del proyecto)
Configurar autoprefixer y postcss-px2rem con las opciones deseadas.
module.exports = {
plugins: {
// Añadir prefijos de proveedor para compatibilidad
autoprefixer: {
overrideBrowserslist: [
"Android 4.1",
"iOS 7.1",
"Chrome > 31",
"ff > 31",
"ie >= 8",
"last 10 versions", // Soporte para las 10 últimas versiones de navegadores principales
],
grid: true,
},
"postcss-px2rem": {
rootValue: 16, // Convierte px a rem: ej. 320px -> 20rem si rootValue es 16
propList: ["*"], // Aplica la conversión a todas las propiedades
// propList: ["*', '!border*'], // Ejemplo: excluir propiedades de borde
unitPrecision: 5, // Precisión decimal para rem
// selectorBlackList: ['.radius'], // Ignora selectores CSS que coincidan (ej. '.fs')
replace: true, // Reemplaza las unidades originales con rem
mediaQuery: false, // Desactiva la conversión en media queries
minPixelValue: 12, // No convierte valores de px menores a 12
},
},
};
// Nota: Si el diseño de referencia es de 375px (ej. para móviles), ajustar `rootValue` a 32.
// Si el diseño es de 750px, `rootValue` debe ser 32.
Este enfoque permite una adaptación consistente del diseño a través de diferentes rseoluciones y niveles de zoom del navegador, asegurando una experiencia de usuario fluida.