W świecie cieniowania, siatek i filtrów Canvas2D może nie wzbudzać entuzjazmu. Ale powinno.
30–40% stron internetowych zawiera element <canvas>
, a 98% wszystkich kanw używa kontekstu renderowania Canvas2D. Canvas2D można znaleźć w samochodach, na lodówkach, a nawet w kosmosie (naprawdę).
Trzeba przyznać, że interfejs API jest nieco przestarzały, jeśli chodzi o najnowocześniejsze rysowanie 2D. Na szczęście ciężko pracowaliśmy nad wdrożeniem nowych funkcji w Canvas2D, aby dorównać CSS, usprawnić ergonomię i poprawić wydajność.
Część 1. Wprowadzenie do CSS
CSS ma kilka poleceń do rysowania, których brakuje w Canvas2D. W ramach nowego interfejsu API dodaliśmy kilka funkcji, o które najczęściej proszono:
Zaokrąglony prostokąt
Zaokrąglone prostokąty: podstawa internetu, komputerów, a także cywilizacji.
Poważnie mówiąc, zaokrąglone prostokąty są bardzo przydatne: jako przyciski, okienka czatu, miniatury, dymki dialogowe – co tylko chcesz. Zaokrąglone prostokąty można było tworzyć w Canvas2D od zawsze, ale było to trochę niechlujne:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'magenta';
const top = 10;
const left = 10;
const width = 200;
const height = 100;
const radius = 20;
ctx.beginPath();
ctx.moveTo(left + radius, top);
ctx.lineTo(left + width - radius, top);
ctx.arcTo(left + width, top, left + width, top + radius, radius);
ctx.lineTo(left + width, top + height - radius);
ctx.arcTo(left + width, top + height, left + width - radius, top + height, radius);
ctx.lineTo(left + radius, top + height);
ctx.arcTo(left, top + height, left, top + height - radius, radius);
ctx.lineTo(left, top + radius);
ctx.arcTo(left, top, left + radius, top, radius);
ctx.stroke();
Wszystko to było konieczne do stworzenia skromnego, prostego zaokrąglonego prostokąta:
W nowym interfejsie API jest metoda roundRect()
.
ctx.roundRect(upper, left, width, height, borderRadius);
Powyższe informacje można całkowicie zastąpić następującymi:
ctx.roundRect(10, 10, 200, 100, 20);
Metoda ctx.roundRect()
przyjmuje też tablicę jako argument borderRadius
z maksymalnie 4 liczbami. Te promienie kontrolują 4 rogi zaokrąglonego prostokąta w taki sam sposób, jak w przypadku CSS. Na przykład:
ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);
Wypróbuj wersję demonstracyjną
Gradient stożkowy
Wyświetlono gradienty liniowe:
const gradient = ctx.createLinearGradient(0, 0, 200, 100);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(0.5, 'magenta');
gradient.addColorStop(1, 'white');
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);
Gradienty promieniowe:
const radialGradient = ctx.createRadialGradient(150, 75, 10, 150, 75, 70);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(0.5, 'magenta');
radialGradient.addColorStop(1, 'lightblue');
ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
A jaki jest gradient stożkowy?
const grad = ctx.createConicGradient(0, 100, 100);
grad.addColorStop(0, 'red');
grad.addColorStop(0.25, 'orange');
grad.addColorStop(0.5, 'yellow');
grad.addColorStop(0.75, 'green');
grad.addColorStop(1, 'blue');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 200);
Modyfikatory tekstu
Canvas2D ma bardzo ograniczone możliwości renderowania tekstu. Do renderowania tekstu w Canvas2D w Chrome dodano kilka nowych atrybutów:
- ctx.letterSpacing
- ctx.wordSpacing
- ctx.fontVariant
- ctx.fontKerning
- ctx.fontStretch
- ctx.textDecoration
- ctx.textUnderlinePosition
- ctx.textRendering
Wszystkie te atrybuty odpowiadają atrybutom usługi porównywania cen o tych samych nazwach.
Część 2. Ulepszenia ergonomii
Wcześniej niektóre rzeczy były możliwe w Canvas2D, ale ich implementacja była niepotrzebnie skomplikowana. Oto kilka ulepszeń dla programistów JavaScript, którzy chcą korzystać z Canvas2D:
Resetowanie kontekstu
Aby wyjaśnić, jak wyczyścić płótno, napisałem głupią funkcję do rysowania wzoru retro:
draw90sPattern();
Świetnie! Skończyłem już z tym wzorem, więc chcę wyczyścić obszar roboczy i narysować coś innego.
Poczekaj, jak zresetować płótno? O tak! ctx.clearRect()
, oczywiście.
ctx.clearRect(0, 0, canvas.width, canvas.height);
Nie udało się. O tak! Najpierw muszę zresetować transformację:
ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);

Super! Pusta przestrzeń do tworzenia. Teraz zacznijmy rysować ładną poziomą linię:
ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();
Grrrr! To nie jest poprawna odpowiedź. 😡 Co to za dodatkowa linia? Dlaczego jest różowy? OK, sprawdźmy Stack Overflow.
canvas.width = canvas.width;
Dlaczego to takie głupie? Dlaczego to takie trudne?
Już nie. Nowy interfejs API to proste, eleganckie i przełomowe rozwiązanie:
ctx.reset();
Przepraszam, że to tyle trwało.
Filtry
Filtry SVG to zupełnie inny świat. Jeśli są one dla Ciebie nowością, zdecydowanie zalecam przeczytanie artykułu The Art Of SVG Filters And Why It Is Awesome, który pokazuje ich niesamowity potencjał.
Filtry stylu SVG są już dostępne w Canvas2D. Wystarczy, że przekażesz filtr jako adres URL wskazujący na inny element filtra SVG na stronie:
<svg>
<defs>
<filter id="svgFilter">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" />
<feConvolveMatrix kernelMatrix="-3 0 0 0 0.5 0 0 0 3" />
<feColorMatrix type="hueRotate" values="90" />
</filter>
</defs>
</svg>
const canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 400;
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
ctx.filter = "url('#svgFilter')";
draw90sPattern(ctx);
Co bardzo psuje nasz wzór:
Co jednak, jeśli chcesz wykonać powyższe czynności, ale nie chcesz używać ciągów znaków w JavaScript? Dzięki nowemu interfejsowi API jest to możliwe.
ctx.filter = new CanvasFilter([
{ filter: 'gaussianBlur', stdDeviation: 5 },
{
filter: 'convolveMatrix',
kernelMatrix: [
[-3, 0, 0],
[0, 0.5, 0],
[0, 0, 3],
],
},
{ filter: 'colorMatrix', type: 'hueRotate', values: 90 },
]);
To bułka z masłem. Wypróbuj i zmodyfikuj parametry w tej wersji demonstracyjnej.
Część 3. Poprawa wydajności
W przypadku nowego interfejsu Canvas2D API chcieliśmy też w miarę możliwości zwiększyć wydajność. Dodaliśmy kilka funkcji, które dają deweloperom większą kontrolę nad ich witrynami i umożliwiają uzyskanie jak najwyższej możliwej liczby klatek na sekundę:
Czytać często
Użyj getImageData()
, aby odczytać dane pikseli z płótna. Może to być bardzo powolne. Nowe API umożliwia jawne oznaczanie płóta do odczytu (np. do efektów generatywnych).
Umożliwi Ci to optymalizację działania pod maską i utrzymanie płynności działania kanwy w różnych przypadkach użycia. Ta funkcja jest dostępna w Firefox od jakiegoś czasu, a w końcu dodaliśmy ją do specyfikacji canvasa.
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
utrata kontekstu,
Zróbmy z smutnych kart znowu szczęśliwe Jeśli klientowi skończy się pamięć GPU lub nastąpi inna katastrofa, możesz teraz otrzymać połączenie zwrotne i w razie potrzeby ponownie narysować obraz:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);
Więcej informacji o kontekście i utracie danych na płótnie znajdziesz w dobrym artykule w wiki WHATWG.
Podsumowanie
Niezależnie od tego, czy dopiero zaczynasz korzystać z Canvas2D, czy używasz go od lat, czy też unikałeś/unikasz go od lat, zachęcam do ponownego przyjrzenia się tej platformie. To interfejs API, który był dostępny od samego początku.