Novas possibilidades no Chrome 65
A API CSS Paint (também conhecida como "CSS Custom Paint" ou "worklet de pintura do Houdini") é ativado por padrão a partir do Chrome 65. O que é? O que você pode fazer a ele? E como funciona? Bom, continue lendo, ok...
A API CSS Paint permite gerar uma imagem de forma programática sempre que um CSS
espera uma imagem. Propriedades como background-image
ou border-image
geralmente são usados com url()
para carregar um arquivo de imagem ou com CSS integrado
funções como linear-gradient()
. Em vez de usá-los, agora você pode usar
paint(myPainter)
para fazer referência a um worklet de pintura.
Como escrever uma worklet de pintura
Para definir um worklet de pintura chamado myPainter
, precisamos carregar um paint CSS
arquivo do worklet usando CSS.paintWorklet.addModule('my-paint-worklet.js')
. Nesse
podemos usar a função registerPaint
para registrar uma classe de worklet de pintura:
class MyPainter {
paint(ctx, geometry, properties) {
// ...
}
}
registerPaint('myPainter', MyPainter);
Dentro do callback paint()
, podemos usar ctx
da mesma forma que faríamos uma
CanvasRenderingContext2D
, como sabemos de <canvas>
. Se você sabe como
desenhar em uma <canvas>
, será possível desenhar em um worklet de pintura. geometry
nos diz
largura e altura da tela que está à nossa disposição. properties
Eu vou
explicar mais adiante neste artigo.
Como um exemplo introdutório, vamos escrever uma worklet de tinta quadriculada e usá-la
como a imagem de plano de fundo de uma <textarea>
. (Estou usando uma área de texto porque é
redimensionável por padrão:
<!-- 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);
Se você já usou o <canvas>
, esse código deve parecer familiar. Consulte
ao vivo
demonstração
aqui.
A diferença do uso de uma imagem de plano de fundo comum aqui é que o padrão serão redesenhadas sob demanda sempre que o usuário redimensionar a área de texto. Isso significa que a imagem de plano de fundo tiver o tamanho exato, incluindo para telas de alta densidade.
Isso é muito legal, mas também é bastante estático. Queríamos escrever um novo worklet toda vez que quiséssemos o mesmo padrão, mas com tamanhos diferentes quadrados? A resposta é não.
Como parametrizar seu worklet
Felizmente, o worklet de pintura pode acessar outras propriedades CSS, que é onde o código
o parâmetro adicional properties
entra em jogo. Ao atribuir à classe uma imagem estática
inputProperties
, será possível se inscrever para receber alterações de qualquer propriedade CSS,
incluindo propriedades personalizadas. Os valores serão fornecidos a você pelo
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);
Agora podemos usar o mesmo código para todos os diferentes tipos de tabuleiros. Mas mesmo melhor, agora podemos acessar DevTools e mudar os valores até encontrarmos o visual certo.
Navegadores que não são compatíveis com o worklet de pintura
Atualmente, apenas o Chrome tem um worklet de pintura implementado. Enquanto há são sinais positivos de todos os outros fornecedores de navegador, não há muito progresso. Para ficar por dentro das novidades, marque Is Houdini Ready Yet? (O Houdini Ready Yet?) regularmente. Enquanto isso, use configurações aprimoramento para manter seu código em execução, mesmo que não haja suporte para paint worklet. Para garantir que tudo funcione como esperado, é preciso ajustar o código dois lugares: o CSS e o JS.
A detecção do suporte ao worklet de pintura no JS pode ser feita verificando o objeto CSS
:
js
if ('paintWorklet' in CSS) {
CSS.paintWorklet.addModule('mystuff.js');
}
Quanto ao CSS, há duas opções. Você pode usar @supports
:
@supports (background: paint(id)) {
/* ... */
}
Um truque mais simples é usar o fato de que o CSS invalida e, posteriormente, ignora uma declaração de propriedade inteira se houver uma função desconhecida nela. Se você especifica uma propriedade duas vezes: primeiro sem o worklet de pintura e depois com o Worklet de pintura, você obtém aprimoramento progressivo:
textarea {
background-image: linear-gradient(0, red, blue);
background-image: paint(myGradient, red, blue);
}
Em navegadores com suporte a worklet de pintura, a segunda declaração de
background-image
vai substituir a primeira. Em navegadores sem suporte
para a worklet de pintura, a segunda declaração é inválida e será descartada,
deixando a primeira declaração em vigor.
Polyfill de pintura CSS
Para muitos usos, também é possível usar o Polyfill de Paint CSS, que adiciona compatibilidade com CSS Custom Paint e Paint Worklets para navegadores modernos.
Casos de uso
Há muitos casos de uso para worklets de pintura, alguns deles mais óbvios do que
outros. Uma das mais óbvias é o uso de uma worklet de tinta para reduzir o tamanho
do seu DOM. Muitas vezes, os elementos são adicionados apenas para criar enfeites
usando CSS. Por exemplo, no Material Design Lite, o botão
com o efeito de ondulação, contém dois elementos <span>
adicionais para implementar o
ondulação. Se você tiver muitos botões, isso pode somar bastante
dos elementos DOM e pode prejudicar o desempenho em dispositivos móveis. Se você
Implementar o efeito de ondulação usando um worklet de pintura
em vez disso, você acabará com 0 elementos adicionais e apenas um worklet de pintura.
Além disso, você tem algo que é muito mais fácil de personalizar e
parametrizar.
Outra vantagem de usar a ferramenta de pintura é que, na maioria dos cenários, uma solução usando um worklet de pintura é pequeno em termos de bytes. Claro, existe uma desvantagem: seu código de pintura será executado sempre que o tamanho da tela ou qualquer os parâmetros mudam. Portanto, se o código for complexo e demorar muito, ele pode introduzir instabilidade. O Chrome está trabalhando para remover os worklets de tinta da linha de execução principal para que mesmo as worklets de pintura de longa duração não afetam a capacidade de resposta do fio
Para mim, o cliente em potencial mais empolgante é que a colcheia de tinta permite que uma polyfilling de recursos CSS que o navegador ainda não tem. Um exemplo seria aos gradientes cônicos de polyfill até eles acessam o Chrome de forma nativa. Outro exemplo: em uma reunião do CSS, decidiu que agora é possível ter várias cores de borda. Durante a reunião em andamento, meu colega Ian Kilpatrick escreveu um polyfill para esse novo CSS usando um worklet de pintura.
Pensar fora da caixa
A maioria das pessoas começa a pensar em imagens de plano de fundo e de borda quando
sobre o worklet de pintura. Um caso de uso menos intuitivo para o worklet de pintura é
mask-image
para que os elementos do DOM tenham formas arbitrárias. Por exemplo, um
diamond:
mask-image
usa uma imagem que tem o tamanho do elemento. Áreas onde
a imagem da máscara é transparente, o elemento é transparente. Áreas em que a máscara é usada
a imagem é opaca e o elemento opaco.
Agora no Chrome
O worklet de pintura está no Chrome Canary há algum tempo. Com o Chrome 65, é ativada por padrão. Vá em frente e experimente as novas possibilidades essa worklet de tinta se abre e mostra o que você construiu! Para mais inspiração, dê uma olhada na coleção de Vincent de Oliveira.