Convertir un objeto de tamaño fijo en un elemento responsivo
Recientemente estuve en una situación en la que quería mostrar un iPhone en un sitio web. Quería que los usuarios pudieran interactuar con una demostración de la aplicación en este teléfono “simulado”, por lo que tenía que representarse en CSS, no en una imagen. Encuentra una gran biblioteca llamada marvelapp/devices.css. La biblioteca implementó el dispositivo que necesitaba con CSS puro y se veían geniales, pero había un problema: los dispositivos que ofrecían no respondían (es decir, no se podían escalar). Un problema abierto enumeraba algunas opciones, pero cada una tenía incompatibilidades de navegador u otros problemas. Me propuse modificar la biblioteca para que los dispositivos respondan.
Aquí está la biblioteca final redimensionable. A continuación, analizaremos el código involucrado en su creación.
La biblioteca original fue escrita en Sass e implementa los dispositivos utilizando elementos con tamaño fijo en píxeles. Los autores también proporcionarán un ejemplo HTML sencillo para cada dispositivo, incluido el iPhone X con el que trabajaremos a lo largo de este artículo. He aquí un vistazo al original. Tenga en cuenta que el dispositivo que representa, aunque detallado, es bastante grande y no cambia de tamaño.
Aquí está el enfoque
Hay tres trucos de CSS que utilicé para cambiar el tamaño de los dispositivos:
calc()
, una función CSS que puede realizar cálculos, incluso cuando las entradas tienen diferentes unidades--size-divisor
, una propiedad personalizada de CSS utilizada con lavar()
función@media
consultas separadas pormin-width
Echemos un vistazo a cada uno de ellos.
cálculo ()
La función CSS calc()
nos permite cambiar el tamaño de los distintos aspectos del dispositivo. La función toma una expresión como entrada y devuelve la evaluación de la función como salida, con las unidades apropiadas. Si quisiéramos hacer que los dispositivos tuvieran la mitad de su tamaño original, necesitaríamos dividir cada medida de píxel en 2.
Antes:
width: 375px;
Después:
width: calc(375px / 2);
El segundo fragmento produce una longitud de la mitad del tamaño del primer fragmento. Cada medida de píxel en la biblioteca original deberá incluirse en una calc()
función para cambiar el tamaño de todo el dispositivo.
var(–divisor-tamaño)
Primero se debe declarar una variable CSS al principio del archivo antes de poder usarla en cualquier lugar.
:root { --size-divisor: 3;}
Con eso, se puede acceder a este valor en toda la hoja de estilo con la función var(). Esto será extremadamente útil ya que queremos que todos los recuentos de píxeles se dividan por el mismo número al cambiar el tamaño de los dispositivos, evitando al mismo tiempo los números mágicos y simplificando el código necesario para que los dispositivos respondan.
width: calc(375px / var(--size-divisor));
Con el valor definido anteriormente, esto devolvería el ancho original dividido por tres, en píxeles.
@medios de comunicacion
Para que los dispositivos respondan, deben responder a los cambios en el tamaño de la pantalla. Esto lo logramos mediante consultas de medios. Estas consultas observan el ancho de la pantalla y se activan si el tamaño de la pantalla cruza umbrales determinados. Elija los puntos de interrupción según los tamaños de Bootstrap para xs
, sm
etc.
No es necesario establecer un punto de interrupción con un ancho mínimo de cero píxeles; en cambio, la :root
declaración al principio del archivo maneja estas pantallas extrapequeñas. Estas consultas de medios deben ir al final del documento y estar ordenadas en orden ascendente de min-width
.
@media (min-width: 576px) { :root { --size-divisor: 2; }}@media (min-width: 768px) { :root { --size-divisor: 1.5; }}@media (min-width: 992px) { :root { --size-divisor: 1; }}@media (min-width: 1200px) { :root { --size-divisor: .67; }}
Cambiar los valores en estas consultas ajustará la magnitud del cambio de tamaño que sufre el dispositivo. Tenga en cuenta que calc()
maneja números flotantes tan bien como números enteros.
Preprocesamiento del preprocesador con Python
Con estas herramientas en mano, necesitaba una manera de aplicar mi nuevo enfoque a la biblioteca de millas de líneas. El archivo resultante comenzará con una declaración de variable, incluyendo la biblioteca completa con cada medida de píxel envuelta en una calc()
función y terminará con las consultas de medios anteriores.
En lugar de preparar los cambios a mano, cree un script en Python que convierta automáticamente todas las medidas de píxeles.
def scale(token): if token[-2:] == ';n': return 'calc(' + token[:-2] + ' / var(--size-divisor));n' elif token[-3:] == ');n': return '(' + token[:-3] + ' / var(--size-divisor));n' return 'calc(' + token + ' / var(--size-divisor))'
Esta función, dada una cadena que contiene NNpx
, devuelve calc(NNpx / var(--size-divisor));
. En todo el archivo, afortunadamente sólo hay tres coincidencias de píxeles: NNpx
, NNpx
; y NNpx
);. El resto es sólo concatenación de cadenas. Sin embargo, estos tokens ya se han generado separando cada línea por caracteres de espacio.
def build_file(scss): out = ':root {nt--size-divisor: 3;n}nn' for line in scss: tokens = line.split(' ') for i in range(len(tokens)): if 'px' in tokens[i]: tokens[i] = scale(tokens[i]) out += ' '.join(tokens) out += "@media (min-width: 576px) {n :root {nt--size-divisor: 2;n }n}nn@media (min-width: 768px) {n :root {nt--size-divisor: 1.5;n }n}nn@media (min-width: 992px) { n :root {nt--size-divisor: 1;n }n}nn@media (min-width: 1200px) { n :root {nt--size-divisor: .67;n }n}" return out
Esta función, que construye la nueva biblioteca, comienza declarando la variable CSS. Luego, recorre toda la biblioteca antigua en busca de medidas de píxeles a escala. Para cada uno de los cientos de tokens que encuentra que contienen px
, escala ese token. A medida que avanza la iteración, la función conserva la estructura de línea original al volver a unir los tokens. Finalmente, agregue las consultas de medios necesarios y devuelva la biblioteca completa como una cadena. Un poco de código para ejecutar las funciones y leer y escribir archivos desde finaliza el trabajo.
if __name__ == '__main__': f = open('devices_old.scss', 'r') scss = f.readlines() f.close() out = build_file(scss) f = open('devices_new.scss', 'w') f.write(out) f.close()
Este proceso crea una nueva biblioteca en Sass. Para crear el archivo CSS para uso final, ejecute:
sass devices_new.scss devices.css
Esta nueva biblioteca ofrece los mismos dispositivos, ¡pero responden! Aquí lo tienes:
Para leer el archivo de salida real, que tiene millas de líneas, consúltelo en GitHub.
Otros enfoques
Si bien los resultados de este proceso son bastante convincentes, fue un poco de trabajo conseguirlos. ¿Por qué no adopté un enfoque más simple? Aquí hay tres enfoques más con sus ventajas e inconvenientes.
zoom
Un enfoque inicialmente prometedor sería utilizar el zoom para escalar los dispositivos. Esto escalaría uniformemente el dispositivo y podría combinarse con consultas de medios como con mi solución, pero funcionaría sin los problemas calc()
y las variables.
Esto no funcionará por una sencilla razón: el zoom es una propiedad no estándar. Entre otras limitaciones, no es compatible con Firefox.
Reemplazar px con em
Otro enfoque de y reemplazar prescribe reemplazar todas las instancias de px con em. Luego, los dispositivos se reducen y crecen según el tamaño de la fuente. Sin embargo, conseguir que sean lo suficientemente pequeños como para caber en la pantalla de un móvil puede requerir tamaños de fuente minúsculos, más pequeños que los tamaños mínimos que imponen los navegadores, como Chrome. Este enfoque también podría generar problemas si un visitante de su sitio web utiliza tecnología de asistencia que aumenta el tamaño de la fuente.
Esto podría solucionarse reduciendo todos los valores en un factor de 100 y luego aplicando tamaños de fuente estándar. Sin embargo, eso requiere tanto preprocesamiento como el enfoque de este artículo, y creo que es más elegante realizar estos cálculos directamente en lugar de manipular los tamaños de fuente.
escalar()
La función scale() puede cambiar el tamaño de objetos completos. La función devuelve una transform-function
, que se puede asignar a un atributo de transformación en un estilo. En general, este es un enfoque sólido, pero no cambia la medida real del dispositivo CSS. Prefiero mi enfoque, especialmente cuando trabajo con elementos complejos de la interfaz de usuario representada en la “pantalla” del dispositivo.
Chris Coyier preparó un ejemplo utilizando este enfoque.
En conclusión
Es posible que cientos de calc()
llamadas no sean la primera herramienta que utilizaría si implementara esta biblioteca desde cero, pero en general, este es un enfoque eficaz para hacer que las bibliotecas existentes sean redimensionables. Agregar variables y consultas de medios hace que los objetos respondan. Si se actualiza la biblioteca subyacente, el script de Python podría procesar estos cambios en una nueva versión de nuestra biblioteca responsable.
Deja un comentario