Interfejs API CSS Paint

Nowe możliwości w Chrome 65

Interfejs API CSS Paint (znany też jako „CSS Custom Paint” lub „worklet do malowania w Houdinie”) jest domyślnie włączony od wersji Chrome 65. Co to jest? Co możesz zrobić z takimi plikami? Jak to działa? No, czytaj dalej.

Interfejs API CSS Paint umożliwia generowanie obrazu za pomocą kodu, gdy właściwość CSS oczekuje obrazu. Właściwości takie jak background-image lub border-image są zwykle używane z url() do wczytywania pliku obrazu lub z wbudowanymi funkcjami CSS, takimi jak linear-gradient(). Zamiast tych właściwości możesz teraz używać właściwości paint(myPainter) do odwoływania się do workletu paint.

Pisanie workleta malowania

Aby zdefiniować element worklet do malowania o nazwie myPainter, musimy załadować plik workletu do malowania CSS za pomocą elementu CSS.paintWorklet.addModule('my-paint-worklet.js'). W tym pliku możemy użyć funkcji registerPaint, aby zarejestrować klasę workletu paint:

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

registerPaint('myPainter', MyPainter);

W ramach wywołania zwrotnego paint() możemy używać funkcji ctx w taki sam sposób jak funkcji CanvasRenderingContext2D, którą znamy z poziomu <canvas>. Jeśli wiesz, jak rysować w <canvas>, możesz rysować w Paint Worklet. geometry podaje nam szerokość i wysokość płótna, które mamy do dyspozycji. properties Omówię to w dalszej części tego artykułu.

Na potrzeby wprowadzenia napiszemy worklet z malowaniem w kratę i użyjemy go jako obrazu tła w komponencie <textarea>. (używam pola tekstowego, ponieważ można je domyślnie zmieniać):

<!-- 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);

Jeśli korzystasz z usługi <canvas>, ten kod powinien Ci być znajomy. Tutaj możesz obejrzeć prezentację na żywo.

Pole tekstowe z wzorem szachownicy jako obrazem tła
Pole tekstowe z wzorem szachownicy jako obrazem tła.

Różnica w porównaniu z użyciem zwykłego obrazu tła polega na tym, że wzór będzie na nowo generowany na żądanie, gdy użytkownik zmieni rozmiar pola tekstowego. Oznacza to, że obraz tła ma zawsze odpowiedni rozmiar, uwzględniający kompensację dla wyświetlaczy o dużej gęstości pikseli.

To świetne, ale też dość statyczne. Czy za każdym razem, gdy chcemy użyć tego samego wzoru, ale z kwadratami o różnej wielkości, chcemy pisać nowy worklet? Odpowiedź brzmi: nie.

Parametryzowanie workletu

Na szczęście moduł malowania może uzyskiwać dostęp do innych właściwości CSS, dlatego właśnie pojawia się dodatkowy parametr properties. Dzięki temu, że klasa ma statyczny atrybut inputProperties, możesz subskrybować zmiany w dowolnej właściwości CSS, w tym w właściwościach niestandardowych. Wartości te będą podawane w parametrze 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);

Teraz możemy używać tego samego kodu do wszystkich rodzajów siatek kontrolnych. Co więcej, możemy teraz otworzyć Narzędzia deweloperskie i zmienić wartości, aż uzyskamy odpowiedni wygląd.

przeglądarki, które nie obsługują workletu paint;

W momencie pisania tego artykułu tylko w Chrome jest zaimplementowany element malowania. Chociaż od innych dostawców przeglądarek otrzymujemy pozytywne sygnały, nie widać znaczących postępów. Aby być na bieżąco, regularnie sprawdzaj Czy Houdini jest już gotowy? Na razie używaj stopniowego ulepszania, aby Twój kod działał, nawet jeśli nie obsługuje paintworkletów. Aby mieć pewność, że wszystko działa zgodnie z oczekiwaniami, musisz dostosować kod w 2 miejscach: w CSS i JS.

Obsługę workletu paint w JS można wykryć, sprawdzając obiekt CSS: js if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('mystuff.js'); } W przypadku CSS masz 2 opcje. Możesz użyć @supports:

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

Bardziej zwięzły trik polega na wykorzystaniu faktu, że CSS unieważnia i ignoruje całą deklarację właściwości, jeśli zawiera ona nieznaną funkcję. Jeśli określisz właściwość dwukrotnie – najpierw bez workleta malowania, a potem z workletem malowania – uzyskasz funkcję ulepszenia progresywnego:

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

W przeglądarkach z obsługą workletu paint druga deklaracja background-image zastąpi pierwszą. W przeglądarkach bez obsługi workletu paint druga deklaracja jest nieprawidłowa i zostaje odrzucona, a pierwsza pozostaje w stanie aktywnym.

Wypełnianie obrazu za pomocą CSS

W wielu przypadkach można też użyć CSS Paint Polyfill, która dodaje obsługę usługi Custom Paint w CSS i Paint Worklets do nowoczesnych przeglądarek.

Przypadki użycia

Worklety do malowania mają wiele zastosowań, niektóre z nich są bardziej oczywiste niż inne. Jednym z bardziej oczywistych sposobów jest użycie workleta do malowania w celu zmniejszenia rozmiaru DOM. Często elementy są dodawane tylko po to, aby utworzyć ozdoby za pomocą CSS. Na przykład w wersji Material Design Lite przycisk z efektem falowania zawiera 2 dodatkowe elementy <span>, które umożliwiają implementację samego efektu falowania. Jeśli masz dużo przycisków, może to spowodować zwiększenie liczby elementów DOM, co może wpłynąć na wydajność na urządzeniach mobilnych. Jeśli zamiast tego wprowadzisz efekt falowania za pomocą workleta malowania, nie będzie żadnych dodatkowych elementów, tylko jeden worklet malowania. Dodatkowo masz coś, co jest znacznie łatwiejsze do dostosowania i parametryzacji.

Kolejną zaletą używania modułu roboczego do malowania jest to, że w większości przypadków rozwiązanie wykorzystujące moduł roboczy do malowania jest niewielkie pod względem liczby bajtów. Oczywiście jest to kompromis: kod paint będzie uruchamiany za każdym razem, gdy zmieni się rozmiar obrazu lub którykolwiek z parametrów. Jeśli kod jest złożony i długi, może to spowodować problemy z przetwarzaniem. Pracujemy nad przeniesieniem workletów do malowania z głównego wątku, aby nawet długotrwałe procesy malowania nie wpływały na szybkość reakcji głównego wątku.

Najbardziej ekscytująca jest dla mnie możliwość wydajnego wypełniania luk w funkcjach CSS, których nie ma jeszcze w przeglądarce. Przykładem może być wypełnianie gradientów stożkowych, dopóki nie zostaną one zaimplementowane natywnie w Chrome. Inny przykład: na spotkaniu dotyczącego CSS postanowiliśmy, że teraz możesz mieć kilka kolorów obramowania. Podczas tego spotkania mój współpracownik Ian Kilpatrick opracował polyfill dla tego nowego zachowania CSS za pomocą workletu do malowania.

Myślenie nieszablonowe

Większość osób zaczyna myśleć o obrazach tła i obrazach obramowania, gdy dowiaduje się o worklecie do malowania. Jednym z mniej intuitywnych zastosowań modułu paint jest mask-image, aby nadać elementom DOM dowolne kształty. Na przykład diament:

Element DOM w kształcie rombu.
Element DOM w kształcie diamentu.

mask-image wykonuje zdjęcie o rozmiarze elementu. Element jest przezroczysty w obszarach, w których obraz maski jest przezroczysty. Obszary, w których obraz maski jest nieprzezroczysty, element jest nieprzezroczysty.

Nowości w Chrome

Worklet Paint jest dostępny w Chrome Canary od jakiegoś czasu. W Chrome 65 jest ona domyślnie włączona. Wypróbuj nowe możliwości, jakie daje to narzędzie, i pokaż nam, co udało Ci się stworzyć. Aby zaczerpnąć więcej inspiracji, zapoznaj się z kolekcją Vincenta De Oliveiry.