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 bardzo 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 i cywilizacji.
Ogólnie rzecz biorąc, zaokrąglone prostokąty są bardzo przydatne: jako przyciski, dymki czatu, miniatury czy dymki. 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();
Do utworzenia skromnego, prostego zaokrąglonego prostokąta potrzebne było wszystko:
W nowym interfejsie API dostępna 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
Zauważyliśmy 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 co z ładnym stożkowym gradientem?
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 słabe 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ą używać Canvas2D:
Resetowanie kontekstu
Aby wyjaśnić, jak wyczyścić płótno, napisałem głupią małą funkcję do rysowania wzoru retro:
draw90sPattern();
Świetnie. Teraz, gdy skończyłem z tym wzorem, chcę wyczyścić płótno i narysować coś innego.
Poczekaj, jak zresetować płótno? O tak! ctx.clearRect()
, oczywiście.
ctx.clearRect(0, 0, canvas.width, canvas.height);
Ech... to nie zadziałało. 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 tu robi ten dodatkowy wiersz? Dlaczego jest różowy? OK, sprawdźmy Stack Overflow.
canvas.width = canvas.width;
Dlaczego to takie głupie? Dlaczego to takie trudne?
Nie ma już problemu. Nowy interfejs API to proste, eleganckie i przełomowe rozwiązanie:
ctx.reset();
Przepraszam, że zajęło to tak długo.
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 zdecydujesz się przekazać filtr jako URL wskazujący 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ć łańcuchów znaków w JavaScript? Dzięki nowemu interfejsowi API jest to w pełni 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 bardzo proste! Wypróbuj i zmodyfikuj parametry tutaj.
Część 3. Poprawa skuteczności
Wprowadzenie nowego interfejsu Canvas2D API zależało również na zwiększeniu wydajności tam, gdzie to możliwe. 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ę:
Będą często czytać
Użyj getImageData()
, aby odczytywać dane pikseli z obiektu canvas. Może to być bardzo powolne. Nowe API umożliwia jawne oznaczanie płótna na potrzeby odczytu (np. do generowania efektów).
W ten sposób zoptymalizujesz ogólne zasoby i zapewnisz szybkość działania obszaru roboczego w wielu różnych zastosowaniach. Ta funkcja jest dostępna w przeglądarce Firefox już od jakiegoś czasu, a właśnie dodajemy ją do specyfikacji canvas.
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ć:
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 na wiki WHATWG.
Podsumowanie
Niezależnie od tego, czy dopiero zaczynasz korzystać z Canvas2D, czy używasz go od lat, czy też unikałeś/unikałaś go przez lata, zachęcam do ponownego przyjrzenia się tej platformie. To interfejs API, który był dostępny od samego początku.