Jij bent het altijd geweest, Canvas2D

Aäron Krajeski
Aaron Krajeski

In een wereld vol shaders, meshes en filters word je misschien niet enthousiast van Canvas2D. Maar het zou moeten! 30-40% van de webpagina's heeft een <canvas> -element en 98% van alle canvassen gebruikt een Canvas2D-renderingcontext. Er zijn Canvas2D's in auto's, op koelkasten en in de ruimte (echt waar).

Toegegeven, de API loopt een beetje achter als het gaat om state-of-the-art 2D-tekenen. Gelukkig hebben we hard gewerkt aan het implementeren van nieuwe functies in Canvas2D om CSS bij te houden, de ergonomie te stroomlijnen en de prestaties te verbeteren.

Deel 1: een inhaalslag maken met CSS

CSS heeft een paar tekenopdrachten die erg ontbreken in Canvas2D. Met de nieuwe API hebben we een handvol van de meest gevraagde functies toegevoegd:

Rond rect

Afgeronde rechthoeken: de hoeksteen van het internet, van computers, van de beschaving.

In alle ernst zijn afgeronde rechthoeken uiterst nuttig: als knoppen, chatballonnen, thumbnails, tekstballonnen, noem maar op. Het is altijd mogelijk geweest om een ​​afgeronde rechthoek te maken in Canvas2D, maar het was alleen een beetje rommelig:

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

Dit alles was nodig voor een bescheiden, eenvoudige afgeronde rechthoek:

Een afgeronde rechthoek.

Met de nieuwe API is er een roundRect() -methode.

ctx.roundRect(upper, left, width, height, borderRadius);

Bovenstaande kan dus geheel vervangen worden door:

ctx.roundRect(10, 10, 200, 100, 20);

De methode ctx.roundRect() neemt ook een array op voor het borderRadius -argument van maximaal vier getallen. Deze stralen bepalen de vier hoeken van de afgeronde rechthoek op dezelfde manier als voor CSS . Bijvoorbeeld:

ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);

Bekijk de demo en speel mee !

Conische verloop

Je hebt lineaire gradiënten gezien:

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

Een lineair verloop.

Radiale hellingen:

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

Een radiale gradiënt.

Maar wat dacht je van een mooi conisch verloop?

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

Een conische gradiënt.

Tekstmodificatoren

De tekstweergavemogelijkheden van Canvas2D lopen hopeloos achter. Chrome heeft verschillende nieuwe kenmerken toegevoegd aan Canvas2D-tekstweergave:

Deze attributen komen allemaal overeen met hun CSS-tegenhangers met dezelfde naam.

Deel 2: ergonomische aanpassingen

Voorheen waren sommige dingen met Canvas2D mogelijk, maar onnodig ingewikkeld om te implementeren. Hier zijn enkele verbeteringen in de levenskwaliteit voor JavaScript-ontwikkelaars die Canvas2D willen gebruiken:

Context opnieuw instellen

Om het opruimen van een canvas uit te leggen, heb ik een gekke kleine functie geschreven om een ​​retropatroon te tekenen :

draw90sPattern();

Een retro patroon van driehoeken en vierkanten.

Geweldig! Nu ik klaar ben met dat patroon, wil ik het canvas leegmaken en iets anders tekenen. Wacht, hoe maken we een canvas weer leeg? Oh ja! ctx.clearRect() natuurlijk.

ctx.clearRect(0, 0, canvas.width, canvas.height);

Hé... dat werkte niet. Oh ja! Ik moet eerst de transformatie resetten:

ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
Een leeg doek.

Perfect! Een mooi leeg canvas. Laten we nu beginnen met het tekenen van een mooie horizontale lijn:

ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();

Een horizontale en een diagonale lijn.

Grrrr! Dat klopt niet! 😡 Wat doet die extra lijn hier? En waarom is het roze? Oké, laten we StackOverflow eens controleren.

canvas.width = canvas.width;

Waarom is dit zo dom? Waarom is dit zo moeilijk?

Nou, dat is het niet meer. Met de nieuwe API hebben we het eenvoudige, elegante en mooie baanbrekende:

ctx.reset();

Sorry dat het zo lang duurde.

Filters

SVG-filters zijn een wereld op zichzelf. Als ze nieuw voor je zijn, raad ik je ten zeerste aan om The Art Of SVG Filters And Why It Is Awesome te lezen, wat een deel van hun verbazingwekkende potentieel laat zien.

SVG-stijlfilters zijn al beschikbaar voor Canvas2D! U hoeft alleen maar bereid te zijn het filter door te geven als een URL die naar een ander SVG-filterelement op de pagina verwijst:

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

Dat verpest ons patroon behoorlijk goed:

Het retropatroon met een wazig effect toegepast.

Maar wat als u het bovenstaande wilt doen, maar binnen JavaScript wilt blijven en niet met strings wilt rommelen? Met de nieuwe API is dit volledig mogelijk.

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 },
]);

Makkelijk als taart! Probeer het en speel met de parameters in de demo hier .

Deel 3: prestatieverbeteringen

Met de nieuwe Canvas2D API wilden we waar mogelijk ook de prestaties verbeteren. We hebben een aantal functies toegevoegd om ontwikkelaars een nauwkeuriger controle over hun websites te geven en de best mogelijke framerates mogelijk te maken:

Zal regelmatig lezen

Gebruik getImageData() om pixelgegevens terug te lezen van een canvas. Het kan erg langzaam zijn. Met de nieuwe API kun je een canvas expliciet markeren om terug te lezen (bijvoorbeeld voor generatieve effecten). Hierdoor kun je de zaken onder de motorkap optimaliseren en het canvas snel houden voor een grotere verscheidenheid aan gebruiksscenario's. Deze functie zit al een tijdje in Firefox en we maken het eindelijk onderdeel van de canvasspecificaties.

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });

Contextverlies

Laten we verdrietige tabbladen weer blij maken! In het geval dat een client geen GPU-geheugen meer heeft of er zich een andere ramp voordoet op uw canvas, kunt u nu teruggebeld worden en indien nodig opnieuw tekenen:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);

Als je meer wilt lezen over canvascontext en verlies, heeft de WHATWG een goede uitleg op hun wiki.

Conclusie

Of je nu nieuw bent bij Canvas2D, je het al jaren gebruikt, of het gebruik al jaren vermijdt, ik ben hier om je te vertellen dat je canvas een andere look moet geven. Het is de API-next-door die er altijd al is geweest.