Tutorial CSS Houdini

¡Buen dia amigos!

¿Qué es un Houdini?


Houdini ( Houdini ): una colección de API de navegador que mejora significativamente el proceso de desarrollo web, incluido el desarrollo de estándares CSS. Los desarrolladores podrán extender CSS usando JavaScript, influyendo en la representación de CSS y diciéndole al navegador cómo se deben aplicar los estilos. Esto proporcionará un aumento significativo en el rendimiento y la estabilidad que el uso de polifilos.

Houdini consta de dos grupos de API: API de alto nivel y API de bajo nivel.

Las API de alto nivel están asociadas con el proceso de renderizado (estilo - diseño - dibujo - composición). Este grupo incluye:

  • Paint API: le permite extender CSS en el paso (es decir, la etapa de representación) de la representación de elementos visuales (color, fondo, bordes, etc.).
  • API de diseño: le permite extender CSS en el paso de determinar el tamaño, la posición y la alineación de los elementos.
  • API de animación: "punto de extensión" en el paso de mostrar y animar elementos.

Las API de bajo nivel son la base de las API de alto nivel e incluyen:

  • API de modelo de objeto escrito
  • API de propiedades y valores personalizados
  • API de métricas de fuente
  • Worklets

Futuro CSS


Houdini, a diferencia del CSS normal, permite a los desarrolladores extender CSS de una manera más natural. ¿Significa esto que las especificaciones CSS dejarán de evolucionar y se adoptarán nuevos estándares? De ningún modo. El objetivo de Houdini es ayudar a desarrollar nuevas funciones CSS mediante la creación de prototipos de trabajo que puedan estandarizarse fácilmente.

Además, los desarrolladores pueden compartir fácilmente worklets CSS abiertos sin preocuparse por la compatibilidad.

API del modelo de objetos con tipo (TOM)


Antes de Houdini, la única forma en que JS y CSS interactuaban era convertir CSS en una cadena y modificarlo. Los estilos de análisis y redefinición manual pueden ser complejos y propensos a errores debido a la necesidad de una doble conversión del tipo de valor (por ejemplo, de un número a una cadena y viceversa). También debe especificar manualmente las unidades de medida para el nuevo valor.

selectedElement.style.fontSize = newFontSize + 'px' // newFontSize = 20
console.log(selectedElement.style.fontSize) // 20px

TOM otorga a las propiedades CSS un significado más semántico al representarlas como objetos JS mecanografiados. Esto mejora enormemente el rendimiento, la estabilidad y facilita el soporte de código. Los valores CSS están representados por la interfaz CSSUnitValue, que consiste en el valor y la propiedad "unidad de medida".

{
  value: 20,
  unit: 'px'
}

Esta interfaz se puede usar con las siguientes características nuevas:

  • computedStyleMap (): para analizar estilos calculados (no integrados). Este método se llama antes de analizar o usar otros métodos.
  • attributeStyleMap: para analizar y modificar estilos en línea. Esta es una propiedad del artículo.

//       ( )
selectedElement.computedStyleMap().get('font-size') // { value: 20, unit: 'px' }

//   
selectedElement.attributeStyleMap.set('font-size', CSS.em(2))
selectedElement. attributeStyleMap.set('color', 'blue')

//    
selectedElement.computedStyleMap().get('font-size') // { value: 20, unit: 'px' }

//    
selectedElement.attributeStyleMap.get('font-size') // { value: 2, unit: 'em' }

Tenga en cuenta cómo se utilizan los tipos CSS al asignar un nuevo valor. El uso de esta sintaxis evita los problemas asociados con los tipos, y el código resultante se vuelve más seguro.

La API en consideración incluye no solo get and set, sino también otros métodos, por ejemplo:

  • clear: elimina todos los estilos en línea
  • eliminar: elimina una propiedad CSS específica y su valor
  • has: devuelve verdadero / falso dependiendo de la disponibilidad de la propiedad especificada
  • agregar: agrega un valor adicional a una propiedad que admite varios valores

Detección


let selectedElement = document.getElementById('example')

if(selectedElement.attributeStyleMap){
  // ...
}

if(selectedElement.computedStyleMap){
  // ...
}

Estado de la especificación


Borrador de trabajo : publicado para discusión comunitaria.

Apoyo


CromoBordeÓperaFirefoxSafari
Apoyado porApoyado porApoyado porNo soportadoApoyo parcial

API de propiedades y valores personalizados


Esta API permite a los desarrolladores ampliar las variables CSS definiendo el tipo, el valor inicial y la herencia. Para definir una propiedad personalizada, es necesario registrarla utilizando el método registerProperty. Este método determina cómo los navegadores deben aplicar la propiedad y manejar los errores.

CSS.registerProperty({
  name: '--colorPrimary',
  syntax: '<color>',
  inherits: false,
  initialValue: 'blue',
})

Un objeto con las siguientes propiedades se pasa a este método como argumento:

  • nombre: nombre de la propiedad personalizada
  • sintaxis: instrucciones para el análisis. Los valores predefinidos son: <color>, <integer>, <number>, <length>, <percentage>, etc.
  • initialValue: valor predeterminado (antes de anular, así como también cuando ocurren errores)

En el ejemplo anterior, se definió una propiedad personalizada de tipo <color>. Esta propiedad se usará para determinar el gradiente. CSS regular no admite transiciones de gradiente. Observe cómo se usará la propiedad personalizada para definir la transición.

.gradient-box {
  background: linear-gradient(45deg, rgba(255, 255, 255, 1) 0% var(--colorPrimary) 60%);
  transition: --colorPrimary 0.5s ease;
  ...
}

.gradient-box:hover {
  --colorPrimary: red;
  ...
}

El navegador no sabe cómo hacer la transición para el degradado, pero sabe cómo hacerlo para el color. Por eso definimos el tipo de propiedad como <color>. En un navegador que admita Houdini, se producirá un cambio de gradiente al pasar el mouse por encima. La posición del gradiente, medida en porcentaje, también se puede cambiar utilizando la propiedad personalizada CSS (registrada como <porcentaje>).

Probablemente en el futuro será posible registrar una propiedad personalizada directamente en CSS.

@property --colorPrimary {
  syntax: '<color>';
  inherits: false;
  initial-value: blue;
}

Ejemplo


Este sencillo ejemplo muestra cómo puede cambiar el color y los puntos de control de un degradado utilizando propiedades CSS personalizadas. Un código de ejemplo se puede encontrar aquí .



Detección


if(CSS.registeredProperty) {
  // ...
}

Estado de la especificación


Borrador de trabajo : publicado para discusión comunitaria.

Apoyo


CromoBordeÓperaFirefoxSafari
Apoyado porApoyado porApoyado porNo soportadoNo soportado

API de métricas de fuente


Esta API se encuentra en una etapa temprana de desarrollo, por lo que la especificación puede cambiar drásticamente en el futuro. Actualmente proporciona métodos para medir el tamaño de los elementos de texto que se muestran en la pantalla, lo que permite a los desarrolladores influir en la representación de los caracteres. Las características CSS existentes no le permiten trabajar con estos valores o hacer que esto funcione muy difícil. Un ejemplo del uso de esta API es el truncamiento dinámico de texto de varias líneas.

Estado de la especificación


Colección de ideas : borrador no publicado.

Los navegadores no son compatibles.

Vorkleta


Antes de pasar a las siguientes API, debe comprender el concepto de worklets. Los vorklets son scripts que se ejecutan durante el renderizado y son independientes del código JS subyacente. Amplían las capacidades del motor de renderizado, están diseñados para ejecución paralela (2 o más instancias), no bloquean el hilo principal, tienen acceso limitado al alcance global y son llamados por el motor según sea necesario. Los vorklets solo se pueden ejecutar a través de HTTPS (en producción) o localhost (para fines de desarrollo y prueba).

Houdini incluye los siguientes worklets que amplían el motor de representación del navegador:

  • Paint Worklet - Paint API
  • Animation Worklet - API de animación
  • Layout Worklet - API de diseño

API de pintura


Paint API permite a los desarrolladores usar funciones JS para pintar el fondo, los bordes o el contenido de un elemento utilizando el Contexto de representación 2D, que es un subconjunto de la API HTML5 Canvas. Paint API utiliza Paint Worklet para dibujar una imagen que depende de los cambios en el CSS (como los cambios en las variables CSS). Quienes estén familiarizados con la API de Canvas se sentirán como en casa con la API de Paint.

La creación de un Worklet de Paint consta de varios pasos:

  1. Escribir y registrar un worklet usando la función registerPaint
  2. Llamar a un worklet en HTML o JS usando CSS.paintWorklet.addModule
  3. Use el método paint () en CSS junto con el nombre del vorklet y los argumentos pasados

Veamos la función registerPaint, que se utiliza para registrar y determinar la funcionalidad del Paint Worklet.

registerPaint('paintWorkletExample', class {
  static get inputProperties() { return ['--myVariable']; }
  static get inputArguments() { return ['<color>']; }
  static get contextOptions() { return {alpha: true} }

  paint(ctx, size, properties, args) {
    // ...
  }
})

La función registerPaint consta de las siguientes partes:

  • inputProperties: una matriz de propiedades CSS personalizadas que observa el worklet. Esta matriz representa las dependencias del worklet.
  • inputArguments: una matriz de argumentos que se pueden pasar de una función a CSS externo
  • contextOptions: transparencia de color. Si es falso, todos los colores serán completamente opacos.
  • paint: la función principal toma los siguientes argumentos:

    • ctx: un contexto de dibujo 2D que es casi idéntico al contexto de lienzo 2D de la API de lienzo
    • tamaño: un objeto con el ancho y la altura del elemento. Los valores dependen del proceso de representación del diseño. El tamaño del lienzo es el mismo que el tamaño real del artículo
    • propiedades: variables contenidas en inputProperties
    • args: una serie de argumentos pasados ​​a la función de pintura

Después de registrar el worklet, debe llamarse en HTML, indicando la ruta al archivo.

CSS.paintWorklet.addModule('path/to/worklet/file.js')

Los Vorklets se pueden agregar desde cualquier fuente externa (por ejemplo, desde CDN), lo que los hace modulares y reutilizables.

CSS.paintWorklet.addModule('https://url/to/worklet/file.js')

Después de llamar al worklet, puede usarse en CSS usando la función "pintar". Esta función, como primer parámetro, toma el nombre registrado del vorklet y todos los argumentos especificados en inputArguments. A partir de este momento, el navegador sabe cuándo llamar al worklet y qué acciones del usuario conducen al cambio de ciertos valores de las propiedades personalizadas de CSS.

.example-element {
  // paintWorkletExample -  
  // blue - ,  
  background: paint(paintWorkletExample, blue);
}

Ejemplo


El siguiente ejemplo demuestra el uso de Paint API, así como la modularidad y reutilización de worklets. Utiliza el worklet ondulado del repositorio de Google Chrome Labs . Ver código de ejemplo aquí .



Detección


if(‘paintWorklet’ in CSS){
  // …
}

@supports(background: paint(paintWorkletExample)){
  // …
}

Estado de la especificación


Recomendación : un borrador de trabajo estable, listo para usar.

Apoyo


CromoBordeÓperaFirefoxSafari
Apoyado porApoyado porApoyado porNo soportadoNo soportado

API de animación


Esta API extiende las animaciones web a través del procesamiento de varios eventos (desplazamiento, desplazamiento, clic, etc.) y mejora el rendimiento al lanzar animaciones en su propia secuencia a través del worklet de animación.

Al igual que cualquier otro worklet, primero debe registrarse una animación de entrenamiento.

registerAnimation(‘animationWorkletExample’, class {
  constructor(options){
    // …
  }
  animate(currentTime, effect){
    // …
  }
})

Esta clase incluye dos funciones:

  • constructor: llamado cuando se crea una nueva instancia. Utilizado para la configuración general.
  • animado: la función principal que contiene la lógica de animación. Se aceptan los siguientes parámetros:

    • currentTime: marcas de tiempo en una línea de tiempo específica
    • efecto: un conjunto de efectos utilizados en la animación

Después del registro, el worklet se incluye en el archivo JS principal, se agrega animación (elemento, marcos, configuraciones) y se adjunta a la línea de tiempo. El concepto de marcas de línea de tiempo y los conceptos básicos de la animación web se encuentra en la siguiente sección.

//   
await CSS.animationWorklet.addModule(‘path/to/worklet/file.js’)

//    
const elementExample = document.getElementById(‘element-example’)

//  
const effectExample = new KeyframeEffect(
  elementExample, //  
  [ // … ], //  
  { // … } //  - , ,    ..
)

//        
new WorkletAnimation(
  ‘animationWorkletExample’ //  
  effectExample, //  
  document.timeline, //  
  {},  //   
).play()

Sellos de tiempo


Las animaciones web se basan en marcas de tiempo, hitos para los efectos en la línea de tiempo de la animación. Por ejemplo, analizamos una animación lineal repetitiva, que consta de tres cuadros (inicio, medio, final), comienza 1 segundo después de que la página está completamente cargada (retraso) y dura 4 segundos.

Las marcas de tiempo de los efectos se verán así (para una animación que dura 4 segundos y sin demora):

Sellos de tiempoMarco de animación
0 msEl primer cuadro: el comienzo de la animación.
2000msEl segundo cuadro: la mitad de la animación.
4000msÚltimo cuadro: final de la animación o restablecer el primer cuadro

effect.localTime con un valor de 3000ms (dado un retraso de 1000ms) une la animación al cuadro promedio en la línea de tiempo (retraso de 1000ms + cuadro promedio de 2000ms). Se logrará el mismo efecto al configurar 7000ms y 11000ms, ya que la animación se repite cada 4000ms.

animate(currentTime, effect){
  effect.localTime = 3000 // 1000ms  + 2000ms  
}

Con un valor constante de effect.localTime, la animación se bloqueará en un cuadro específico. Por lo tanto, el valor de effect.localTime debería cambiar. Este valor debe ser una función vinculada a currentTime u otra variable.

Así es como se ve el código de animación lineal:

animate(currentTime, effect){
  effect.localTime = currentTime // y = x  
}

Cronología (document.timeline)Sello de tiempoMarco
startTime + 0ms (tiempo transcurrido)startTime + 0msEl primero
startTime + 1000ms (tiempo transcurrido)startTime + 1000ms (retraso) + 0msEl primero
startTime + 3000ms (tiempo transcurrido)startTime + 1000ms (retraso) + 2000msMedio
startTime + 5000ms (tiempo transcurrido)startTime + 1000ms (retraso) + 4000msÚltimo primero
startTime + 7000ms (tiempo transcurrido)startTime + 1000ms (retraso) + 6000msMedio
startTime + 9000ms (tiempo transcurrido)startTime + 1000ms (retraso) + 8000msÚltimo primero

Las marcas de tiempo no se limitan a 1: 1. La API de animación permite a los desarrolladores manipular marcas a través de la función animada, utilizando funciones JS estándar para crear efectos complejos. Las animaciones también pueden variar en cada iteración (con animaciones repetibles).

La animación puede estar vinculada no solo a la carga de documentos, sino también a las acciones del usuario. Una acción del usuario, como desplazar una página, se puede usar en la animación a través de un objeto ScrollTimeline. Por ejemplo, una animación puede comenzar cuando se desplaza 200 px y finalizar cuando se desplaza 800 px.

const scrollTimelineExample = new ScrollTimeline({
  scrollSource: scrollElement, // ,     
  orientation: ‘vertical’, //  
  startScrollOffset: ‘200px’, //  
  endScrollOffset: ‘800px’, //  
  timeRange: 1200, //  
  fill: ‘forwards’ //  
})

La animación se adapta automáticamente a la velocidad de desplazamiento, sin dejar de ser suave y receptiva. Dado que la animación se ejecuta en su propia secuencia y se conecta al motor de representación del navegador, se inicia sin problemas y no afecta el rendimiento.

Ejemplo


El siguiente ejemplo muestra animación no lineal. Utiliza la función gaussiana con la misma rotación de tiempo de ida y vuelta. Ver código de ejemplo aquí .



Detección


if(CSS.animationWorklet){
  // …
}

Estado de la especificación


Primer borrador de trabajo público : listo para la discusión comunitaria, sujeto a cambios en el futuro.

Apoyo


CromoBordeÓperaFirefoxSafari
Apoyo parcialApoyo parcialApoyo parcialNo soportadoNo soportado

API de diseño


La API de diseño permite a los desarrolladores ampliar el proceso de representación del diseño mediante la definición de nuevos módulos para su uso en la propiedad de "visualización" CSS. Esta API presenta nuevos conceptos, es muy compleja y ofrece una gran cantidad de configuraciones para desarrollar algoritmos personalizados para trabajar con el diseño de página.

Lo primero es lo primero, un worklet necesita ser registrado.

registerLayout(‘exampleLayout’, class{
  static get inputProperties() { return [‘--example-variable’] }

  static get childrenInputProperties() { return [‘--exampleChildVariable’] }

  static get layoutOptions(){
    return {
      childDisplay: ‘normal’,
      sizing: ‘block-like’
    }
  }

  intrinsicSizes(children, edges, styleMap){
    // …
  }

  layout(children, edges, constraints, styleMap, breakToken){
    // …
  }
})

El registro de un worklet incluye los siguientes métodos:

  • inputProperties: una matriz de propiedades CSS personalizadas que el worklet está observando y que pertenecen al padre, el elemento que causó el diseño. Esta matriz representa las dependencias de diseño.
  • childrenInputProperties: una matriz de propiedades CSS personalizadas que el widget observa y que pertenecen al elemento secundario
  • layoutOptions: define las siguientes propiedades de diseño:

    • childDisplay: los valores predefinidos son block y normal. Define cómo se muestra el elemento (bloque o línea)
    • dimensionamiento: Los valores predefinidos son como bloques y manuales. Determina la necesidad de un cálculo preliminar de los tamaños de los elementos (si no se especifica)
  • intrinsicSizes: define cómo se muestra el contenedor o su contenido en el contexto del diseño:

    • children: hijo del elemento que causó que se representara el diseño de la página
    • bordes: bordes del contenedor
    • styleMap: modelo de objeto de estilo de contenedor escrito
  • diseño: la función principal para trabajar con el diseño:

    • hijos: elemento hijo
    • bordes: bordes
    • restricciones: restricciones impuestas por el diseño primario
    • styleMap: modelo de objeto de estilo de contenedor escrito
    • breakToken: punto de interrupción para paginación o división de diseño de impresión

A continuación, el worklet se agrega al archivo HTML o JS.

CSS.layoutWorklet.addModule(‘path/to/worklet/file.js’)

Hacemos un enlace al worklet en el archivo con estilos.

.example-element {
  display: layout(exampleLayout)
}

Cómo funciona la API de diseño con diseño


En el ejemplo anterior, definimos exampleLayout.

Un elemento .example se denomina diseño primario, que incluye sangrías, bordes y controles deslizantes de desplazamiento. Un diseño primario consta de elementos secundarios llamados diseños actuales. Los diseños actuales son elementos de destino cuyos diseños están "personalizados" utilizando la API de diseño. Por ejemplo, cuando se usa "display: flex", los descendientes del elemento se reorganizan de acuerdo con el diseño flexible. Esto es similar al funcionamiento de la API de diseño.

Cada diseño actual consta de diseños secundarios que contienen algoritmos para representar el diseño del elemento secundario: LayoutChild (incluidas las pseudo-clases :: before y :: after). LayoutChild: un contenedor generado por herramientas CSS que contiene datos sobre estilos (sin datos sobre el diseño). El navegador crea automáticamente elementos LayoutChild en la etapa de aplicación de estilos. Un diseño secundario puede crear un Fragmento que contiene instrucciones para representar el diseño.

Ejemplo


Este ejemplo también usa el repositorio de Google Chrome Labs , pero el texto se reemplaza con imágenes. Ver código de ejemplo aquí .



Detección


if(CSS.layoutWorklet){
  // …
}

Estado de la especificación


Primer borrador de trabajo público : listo para la discusión comunitaria, sujeto a cambios en el futuro.

Apoyo


CromoBordeÓperaFirefoxSafari
Apoyo parcialApoyo parcialApoyo parcialNo soportadoNo soportado

Houdini y mejora progresiva


A pesar de que Houdini actualmente no tiene un soporte de navegador óptimo, se puede usar para una mejora progresiva. Si no está familiarizado con el concepto de mejora progresiva, le aconsejo que lea este artículo . Al usar Houdini, se debe considerar lo siguiente:

Identifique siempre el soporte para evitar errores.


Cada API y Worklet de Houdini tiene una manera fácil de verificar la accesibilidad. Esto evita los problemas de usar Houdini en navegadores que aún no admiten esta tecnología.

Use Houdini para mejorar la visualización y la visualización.


Los usuarios que utilizan navegadores que no son compatibles con Houdini deben tener acceso al contenido y la funcionalidad básica del sitio. La experiencia del usuario y la visualización del contenido no deberían depender de Houdini.

Use CSS estándar como alternativa


Por ejemplo, las propiedades CSS personalizadas se pueden usar como una alternativa a las propiedades personalizadas y los valores API.

Concéntrese en desarrollar aplicaciones productivas y confiables, utilizando Houdini con fines decorativos solo como una mejora progresiva.

Conclusión


Houdini permite a los desarrolladores usar código JS para trabajar con estilos en el proceso de renderizado, aumentando el rendimiento y la estabilidad de la aplicación. La capacidad de incrustar código en el proceso de renderizado le permite crear polífilos CSS que son fáciles de compartir con otros, aplicar y posiblemente incluso incluir en la especificación. Además, Houdini permite a los desarrolladores y diseñadores ser menos dependientes de las restricciones de CSS cuando trabajan con estilos, diseños y animaciones.

Houdini se puede usar ahora, pero solo como una mejora progresiva. Esto permitirá que los navegadores que no admiten Houdini muestren páginas sin errores, proporcionando una experiencia de usuario óptima.

No puedo esperar hasta que la comunidad de desarrolladores pueda disfrutar plenamente de las capacidades de Houdini. Aquí hay un par de ejemplos:

Caducidad de CSS Houdini
Una introducción interactiva a CSS Houdini Ejemplos de Houdini
de Google Chrome Labs

Referencias


Borradores del W3C del folleto de animación Houdini
Houdini Specification (Chrome Dev Summit 2018)
- Desarrolladores de Google

Gracias por su atención.

All Articles