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? Czytaj dalej, cóż...

Interfejs API usługi 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(), aby wczytać plik 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.

Tworzenie 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. propertiesWyjaśnię to w dalszej części tego artykułu.

Na potrzeby wprowadzenia napiszemy worklet z malowaniem kratki i użyjemy go jako obrazu tła <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 wcześniej korzystałeś(-aś) z usługi <canvas>, ten kod powinien być Ci znajomy. Tutaj możesz obejrzeć prezentację na żywo.

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

Różnica w stosunku do wspólnego obrazu tła polega na tym, że wzór będzie ponownie rysowany na żądanie, gdy użytkownik zmieni rozmiar obszaru tekstowego. Oznacza to, że obraz tła zawsze jest odpowiednio duży. Dotyczy to również kompensacji w przypadku wyświetlaczy o dużej gęstości.

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 zostaną podane 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 kompaktowa sztuczka polega na tym, że CSS unieważnia, a następnie 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 Worklets w nowoczesnych przeglądarkach.

Przypadki użycia

Worklety do malowania mają wiele zastosowań, niektóre z nich są bardziej oczywiste niż inne. Jednym z bardziej oczywistych jest użycie Workleta malowania w celu zmniejszenia rozmiaru DOM. Często elementy są dodawane tylko po to, aby utworzyć ozdoby za pomocą CSS. Na przykład w Material Design Lite przycisk z efektem fali zawiera 2 dodatkowe elementy <span> służące do zaimplementowania samej fali. Jeśli masz dużo przycisków, może to skutkować sporą liczbą elementów DOM i spowodować pogorszenie działania 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 trudna jest pewnego rodzaju kompromis: kod malowania jest uruchamiany zawsze, gdy zmieni się rozmiar obszaru roboczego lub dowolny z jego parametrów. Jeśli kod jest złożony i długi, może to spowodować problemy z przetwarzaniem. Chrome pracuje nad przeniesieniem Workletów malowania z wątku głównego, aby nawet długotrwałe Worklety malowania nie wpływały na responsywność wątku głównego.

Moim zdaniem najbardziej ekscytujący jest fakt, że Worklet umożliwia efektywne wypełnianie funkcji CSS, których nie ma jeszcze przeglądarka. 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-imageprzypisywanie elementom DOM dowolnych kształtów. Na przykład diament:

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

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

Teraz w Chrome

Worklet jest od jakiegoś czasu w Chrome Canary. 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 znaleźć więcej inspiracji, zobacz kolekcję Vincenta De Oliveiry.