API de Paint de CSS

Nuevas posibilidades en Chrome 65

La API de CSS Paint (también conocida como "CSS Custom Paint" o "Houdini's Paint worklet") es habilitado de forma predeterminada a partir de Chrome 65. ¿Qué es? ¿Qué puedes hacer? con él? ¿Cómo funciona? Bueno, sigue leyendo, ¿sí?

La API de pintura de CSS te permite generar una imagen de manera programática cada vez que se crea un CSS espera una imagen. Propiedades como background-image o border-image Por lo general, se usan con url() para cargar un archivo de imagen o con CSS integrado. funciones como linear-gradient(). En lugar de usarlos, ahora puedes usar paint(myPainter) para hacer referencia a un worklet de pintura.

Cómo escribir un worklet de pintura

Para definir un worklet de pintura llamado myPainter, debemos cargar un elemento de pintura de CSS. un archivo worklet con CSS.paintWorklet.addModule('my-paint-worklet.js'). En ese podemos usar la función registerPaint para registrar una clase de worklet de pintura:

class MyPainter {
  paint(ctx, geometry, properties) {
    // ...
  }
}

registerPaint('myPainter', MyPainter);

Dentro de la devolución de llamada paint(), podemos usar ctx de la misma manera que lo haríamos con un CanvasRenderingContext2D, tal como la conocemos por <canvas>. Si sabes cómo dibuja en un <canvas>, puedes dibujar en un worklet de pintura. geometry nos indica la ancho y alto del lienzo que tenemos a nuestra disposición. properties Voy a más adelante en este artículo.

Como ejemplo introductorio, escribamos un worklet de pintura de tablero de ajedrez y lo usemos como imagen de fondo de un <textarea>. (Estoy usando un área de texto porque es puede cambiar de tamaño de forma predeterminada):

<!-- index.html -->
<!doctype html>
<style>
  textarea {
    background-image: paint(checkerboard);
  }
</style>
<textarea></textarea>
<script>
  CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
  paint(ctx, geom, properties) {
    // Use `ctx` as if it was a normal canvas
    const colors = ['red', 'green', 'blue'];
    const size = 32;
    for(let y = 0; y < geom.height/size; y++) {
      for(let x = 0; x < geom.width/size; x++) {
        const color = colors[(x + y) % colors.length];
        ctx.beginPath();
        ctx.fillStyle = color;
        ctx.rect(x * size, y * size, size, size);
        ctx.fill();
      }
    }
  }
}

// Register our class under a specific name
registerPaint('checkerboard', CheckerboardPainter);

Si ya usaste <canvas>, este código debería resultarte familiar. Consulta en vivo demostración aquí.

Área de texto con un patrón de damas como imagen de fondo
Área de texto con un patrón de damas como imagen de fondo.

La diferencia de usar una imagen de fondo común aquí es que el patrón se volverá a dibujar a pedido cada vez que el usuario cambie el tamaño del área de texto. Esto significa la imagen de fondo siempre sea exactamente tan grande como debe ser, con las adicional para pantallas de alta densidad.

Eso es genial, pero también es bastante estático. ¿Quisiéramos escribir un nuevo cada vez que queríamos el mismo patrón, pero con tamaños diferentes cuadrados? La respuesta es no.

Parametrización de tu worklet

Por suerte, el worklet de pintura puede acceder a otras propiedades de CSS, que es donde la entra en juego el parámetro adicional properties. Si le das a la clase un valor inputProperties, puedes suscribirte a los cambios realizados en cualquier propiedad de CSS incluidas las propiedades personalizadas. Los valores se te proporcionarán a través del Parámetro properties.

<!-- index.html -->
<!doctype html>
<style>
  textarea {
    /* The paint worklet subscribes to changes of these custom properties. */
    --checkerboard-spacing: 10;
    --checkerboard-size: 32;
    background-image: paint(checkerboard);
  }
</style>
<textarea></textarea>
<script>
  CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
  // inputProperties returns a list of CSS properties that this paint function gets access to
  static get inputProperties() { return ['--checkerboard-spacing', '--checkerboard-size']; }

  paint(ctx, geom, properties) {
    // Paint worklet uses CSS Typed OM to model the input values.
    // As of now, they are mostly wrappers around strings,
    // but will be augmented to hold more accessible data over time.
    const size = parseInt(properties.get('--checkerboard-size').toString());
    const spacing = parseInt(properties.get('--checkerboard-spacing').toString());
    const colors = ['red', 'green', 'blue'];
    for(let y = 0; y < geom.height/size; y++) {
      for(let x = 0; x < geom.width/size; x++) {
        ctx.fillStyle = colors[(x + y) % colors.length];
        ctx.beginPath();
        ctx.rect(x*(size + spacing), y*(size + spacing), size, size);
        ctx.fill();
      }
    }
  }
}

registerPaint('checkerboard', CheckerboardPainter);

Ahora podemos usar el mismo código para todos los tipos diferentes de tableros de ajedrez. Pero incluso mejor, ahora podemos ir a Herramientas para desarrolladores y controlar los valores. hasta encontrar el estilo adecuado.

Navegadores que no admiten el worklet de pintura

Al momento de la redacción, solo Chrome tiene implementado el worklet de pintura. Mientras estás allí son señales positivas de todos los demás proveedores de navegadores, no hay mucho progreso. Para mantenerte al tanto, consulta el artículo Is Houdini Ready Yet? con regularidad. Mientras tanto, asegúrate de usar la para mantener tu código en ejecución incluso si no hay compatibilidad con pintura. worklet. Para asegurarte de que todo funcione como se espera, debes ajustar el código de dos lugares: el CSS y el JS.

Para detectar la compatibilidad con el worklet de pintura en JS, se puede verificar el objeto CSS: js if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('mystuff.js'); } En cuanto al CSS, tiene dos opciones. Puedes usar @supports:

@supports (background: paint(id)) {
  /* ... */
}

Un truco más compacto es usar el hecho de que CSS invalida y, luego, ignora una declaración de propiedad completa si contiene una función desconocida. Si especificas una propiedad dos veces: primero sin worklet de pintura y, luego, con el Paint worklet, obtienes mejora progresiva:

textarea {
  background-image: linear-gradient(0, red, blue);
  background-image: paint(myGradient, red, blue);
}

En navegadores compatibles con el worklet de pintura, la segunda declaración del background-image reemplazará al primero. En navegadores sin compatibilidad para el worklet de pintura, la segunda declaración no es válida y se descartará, y deja la primera declaración vigente.

Polyfill de pintura de CSS

Para muchos usos, también es posible utilizar Polifill de pintura de CSS que agrega compatibilidad con los Worklets personalizados de pintura y pintura de CSS a los navegadores modernos.

Casos de uso

Hay muchos casos de uso para los worklets de pintura, algunos de ellos más obvios que otras personas. Uno de los más obvios es usar el worklet de pintura para reducir el tamaño de tu DOM. A menudo, los elementos se agregan exclusivamente para crear adornos. con CSS. Por ejemplo, en Material Design Lite, el botón con el efecto dominó contiene 2 elementos <span> adicionales para implementar la Ondas a sí misma. Si tienes muchos botones, esto puede aumentar de elementos del DOM y puede llevar a un rendimiento degradado en los dispositivos móviles. Si implementar el efecto de ondas con el worklet de pintura en cambio, terminas con 0 elementos adicionales y solo un worklet de pintura. Además, hay algo mucho más fácil de personalizar parametrizar.

Otra ventaja de usar el worklet de pintura es que, en la mayoría de los escenarios, es una solución con el worklet de pintura es pequeño en términos de bytes. Por supuesto, el código de pintura se ejecutará cada vez que el tamaño del lienzo o cualquiera de cambian los parámetros. Por lo tanto, si tu código es complejo y tarda mucho, podría introducir bloqueos. Chrome está trabajando para quitar los conjuntos de pintura del subproceso principal para que incluso los trabajos de pintura de larga duración no afectan la capacidad de respuesta de la conversación.

Para mí, el cliente potencial más interesante es que el worklet de pintura polyfill de funciones de CSS que el navegador aún no posee. Un ejemplo sería a polyfills gradientes cónicos hasta que llegan a Chrome de forma nativa. Otro ejemplo: en una reunión de CSS, decidió que ahora puedes tener varios colores de borde. Durante esta reunión sigue en curso, mi colega Ian Kilpatrick escribió un polyfill para este nuevo CSS con el worklet de pintura.

Pensar de manera innovadora.

La mayoría de las personas empieza a pensar en las imágenes de fondo y de borde cuando aprender sobre el worklet de pintura. Un caso de uso menos intuitivo para el worklet de pintura es mask-image para hacer que los elementos del DOM tengan formas arbitrarias. Por ejemplo, un diamante:

Elemento del DOM en forma de diamante.
Elemento del DOM en forma de diamante.

mask-image toma una imagen del tamaño del elemento. Áreas en las que de enmascaramiento es transparente, el elemento es transparente. Áreas en las que se coloca la mascarilla es opaca, el elemento opaco.

Ahora en Chrome

El worklet de pintura está en Chrome Canary desde hace un tiempo. Con Chrome 65, es habilitado de forma predeterminada. Prueba las nuevas posibilidades se abrirá ese worklet de pintura y nos mostrará lo que construiste. Para obtener más inspiración, echa un vistazo a la colección de Vincent De Oliveira.