Interfejs API CSS Paint

Nowe możliwości w Chrome 65

Interfejs CSS Paint API (nazywany też „CSS Custom Paint” lub „Worklet malowania Houdini”) jest domyślnie włączony od Chrome 65. Co to jest? Co można z nim zrobić? Jak to działa? Czytaj dalej, cóż...

Interfejs CSS Paint API umożliwia automatyczne generowanie obrazu za każdym razem, gdy właściwość CSS oczekuje obrazu. Właściwości takie jak background-image czy border-image są zwykle używane z właściwością url() do wczytywania pliku graficznego lub z wbudowanymi funkcjami CSS, takimi jak linear-gradient(). Zamiast ich używać możesz teraz używać parametru paint(myPainter), aby odwołać się do sprawdzonego wyrenderowania.

Pisanie farby

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

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

registerPaint('myPainter', MyPainter);

W wywołaniu zwrotnym paint() możemy używać elementu ctx w taki sam sposób jak z metody CanvasRenderingContext2D znanej z <canvas>. Jeśli umiesz rysować w <canvas>, możesz to zrobić na malowaniu. geometry określa szerokość i wysokość obszaru roboczego, który mamy do dyspozycji. propertiesWyjaśnię to w dalszej części tego artykułu.

Na początek napiszmy zestaw technik malowania w szachownicę i użyjemy go jako obrazu tła <textarea>. (Używam obszaru tekstowego, ponieważ domyślnie można go zmienić).

<!-- 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 zdarzyło Ci się już korzystać z usługi <canvas>, ten kod powinien wyglądać znajomo. Zobacz prezentację na żywo tutaj.

Pole tekstowe ze wzorem w szachownicę jako obraz tła
Obszar tekstowy ze wzorem w szachownicę 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 całkiem fajne, ale też dość statyczne. Czy chcielibyście pisać nowy zbiór zadań za każdym razem, gdy szukaliśmy tego samego wzoru, ale z kwadratami o innych rozmiarach? Odpowiedź brzmi: nie.

Parametrowanie Worklet

Na szczęście farba ma dostęp do innych właściwości CSS, więc potrzebny jest dodatkowy parametr properties. Nadając klasie atrybut statyczny inputProperties, możesz subskrybować zmiany dowolnej właściwości CSS, w tym właściwości niestandardowych. Wartości te otrzymasz 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 różnych rodzajów szachownicy. Jeszcze lepiej, że możemy teraz skorzystać z Narzędzi deweloperskich i eksperymentować z wartościami, aż znajdziemy właściwy wygląd.

Przeglądarki, które nie obsługują Workletu malowania

W momencie pisania tego tekstu tylko Chrome miał zaimplementowany obszar roboczy malowania. Choć od wszystkich pozostałych dostawców przeglądarek znajdują się pozytywne sygnały, nie mamy większych postępów. Sprawdzaj regularnie, czy jest już gotowy?. Tymczasem korzystaj z progresywnego ulepszania, aby kod działał nawet wtedy, gdy nie obsługujesz malowania. Aby wszystko działało zgodnie z oczekiwaniami, musisz dostosować kod w 2 miejscach: CSS i JS.

Obsługę malowania w JS można wykryć za pomocą obiektu CSS: js if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('mystuff.js'); } W przypadku CSS masz 2 opcje. Elementów @supports możesz używać:

@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ść 2 razy – najpierw bez malowania, a później używając farby, uzyskasz stopniowe ulepszenie:

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

W przeglądarkach z obsługą Workletu malowania druga deklaracja parametru background-image zastąpi pierwszą. W przeglądarkach bez obsługi Workletu malowania druga deklaracja jest nieprawidłowa i zostanie odrzucona, bez zastosowania pierwszej deklaracji.

Polyfill CSS z farbą

W wielu zastosowaniach można też skorzystać z szablonu CSS Paint Polyfill, który dodaje obsługę niestandardowych procesów renderowania i malowania CSS w nowoczesnych przeglądarkach.

Przykłady zastosowań

Istnieje wiele przypadków użycia Workletów malowania, niektóre z nich są bardziej oczywiste niż inne. Jednym z bardziej oczywistych sposobów jest użycie Workletu malowania w celu zmniejszenia rozmiaru DOM. Często dodaje się je tylko po to, aby za pomocą CSS utworzyć ozdoby. 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 stanowić sporą liczbę elementów DOM i spowodować spadek wydajności na urządzeniach mobilnych. Jeśli zamiast tego zaimplementujesz efekt fali za pomocą narzędzia malowania, nie będziesz mieć żadnych dodatkowych elementów i tylko 1 worklet malowania. Poza tym masz kod, który znacznie łatwiej dostosować i parametryzować.

Kolejną zaletą korzystania z malowania Worklet jest to, że w większości przypadków rozwiązanie z użyciem paintletu malowania jest małe 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 zajmuje dużo czasu, może powodować zacinanie. 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ć stosowanie gradientów stożkowych, aż trafią bezpośrednio w Chrome. Inny przykład: na spotkaniu usługi porównywania cen ustaliliśmy, że możesz mieć wiele kolorów obramowania. Gdy to spotkanie nadal trwało, mój kolega Ian Kilpatrick napisał kod Polyfill dla nowego zachowania CSS za pomocą farby.

Nieszablonowe myślenie

Gdy uczymy się malowania, większość osób zaczyna myśleć o obrazach tła i obramowaniu. Mniej intuicyjnym przypadkiem użycia narzędzia malowania jest mask-image, dzięki któremu elementy DOM mają dowolne kształty. Na przykład diament:

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

mask-image pobiera obraz, który ma rozmiar elementu. Obszary, w których obraz maski jest przezroczysty, a element jest przezroczysty. Obszary, w których obraz maski jest nieprzezroczysty, a 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, które otwierają się w narzędziu malowanie, i pokaż nam, co udało Ci się stworzyć. Aby znaleźć więcej inspiracji, zobacz kolekcję Vincenta De Oliveiry.