Anwendung von Stilen und Farben
In dem Kapitel über Zeichnen von Formen haben wir nur die standardmäßigen Linien- und Füllstile verwendet. Hier werden wir die Canvas-Optionen untersuchen, die uns zur Verfügung stehen, um unsere Zeichnungen etwas attraktiver zu gestalten. Sie lernen, wie Sie Ihren Zeichnungen verschiedene Farben, Linienstile, Verläufe, Muster und Schatten hinzufügen können.
Hinweis: Canvas-Inhalte sind für Screenreader nicht zugänglich. Wenn der Canvas rein dekorativ ist, fügen Sie role="presentation"
zum öffnenden Tag des <canvas>
hinzu. Andernfalls fügen Sie beschreibenden Text als Wert des aria-label
Attributs direkt im Canvas-Element hinzu oder fügen Sie Fallback-Inhalte innerhalb des öffnenden und schließenden Canvas-Tags hinzu. Canvas-Inhalte sind nicht Teil des DOM, aber verschachtelte Fallback-Inhalte schon.
Farben
Bisher haben wir nur Methoden des Zeichenkontexts gesehen. Wenn wir Farben auf eine Form anwenden möchten, gibt es zwei wichtige Eigenschaften, die wir verwenden können: fillStyle
und strokeStyle
.
fillStyle = color
-
Legt den Stil fest, der beim Füllen von Formen verwendet wird.
strokeStyle = color
-
Legt den Stil für die Umrisse von Formen fest.
color
ist ein String, der eine CSS <color>
, ein Verlauf-Objekt oder ein Muster-Objekt darstellt. Wir werden später auf Verlauf- und Muster-Objekte eingehen. Standardmäßig sind die Strich- und Füllfarbe auf Schwarz (CSS-Farbwert #000000
) eingestellt.
Hinweis: Wenn Sie die Eigenschaft strokeStyle
und/oder fillStyle
setzen, wird der neue Wert zum Standard für alle danach gezeichneten Formen. Für jede Form, die Sie in einer anderen Farbe haben möchten, müssen Sie die Eigenschaft fillStyle
oder strokeStyle
neu zuweisen.
Die gültigen Strings, die Sie eingeben können, sollten laut der Spezifikation CSS <color>
Werte sein. Jedes der folgenden Beispiele beschreibt die gleiche Farbe.
// these all set the fillStyle to 'orange'
ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255 165 0)";
ctx.fillStyle = "rgb(255 165 0 / 100%)";
Ein fillStyle
-Beispiel
In diesem Beispiel verwenden wir erneut zwei for
Schleifen, um ein Raster von Rechtecken zu zeichnen, jedes in einer anderen Farbe. Das resultierende Bild sollte etwa wie der Screenshot aussehen. Hier passiert nichts spektakuläres. Wir verwenden die beiden Variablen i
und j
, um eine eindeutige RGB-Farbe für jedes Quadrat zu generieren und ändern nur die Rot- und Grünwerte. Der Blaukanal hat einen festen Wert. Durch Ändern der Kanäle können Sie alle Arten von Paletten generieren. Durch Erhöhen der Schritte können Sie etwas erreichen, das wie die Farbpaletten von Photoshop aussieht.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
for (let i = 0; i < 6; i++) {
for (let j = 0; j < 6; j++) {
ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)} ${Math.floor(
255 - 42.5 * j,
)} 0)`;
ctx.fillRect(j * 25, i * 25, 25, 25);
}
}
}
Das Ergebnis sieht so aus:
Ein strokeStyle
-Beispiel
Dieses Beispiel ähnelt dem obigen, verwendet aber die strokeStyle
-Eigenschaft, um die Farben der Umrisse der Formen zu ändern. Wir verwenden die Methode arc()
, um Kreise anstelle von Quadraten zu zeichnen.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
for (let i = 0; i < 6; i++) {
for (let j = 0; j < 6; j++) {
ctx.strokeStyle = `rgb(0 ${Math.floor(255 - 42.5 * i)} ${Math.floor(
255 - 42.5 * j,
)})`;
ctx.beginPath();
ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, 2 * Math.PI, true);
ctx.stroke();
}
}
}
Das Ergebnis sieht so aus:
Transparenz
Zusätzlich zum Zeichnen opaker Formen auf dem Canvas können wir auch halbtransparente (oder transluzente) Formen zeichnen. Dies geschieht entweder durch Setzen der Eigenschaft globalAlpha
oder durch Zuweisen einer halbtransparenten Farbe zum Strich- und/oder Füllstil.
globalAlpha = transparencyValue
-
Wendet den angegebenen Transparenzwert auf alle zukünftigen Formen an, die auf dem Canvas gezeichnet werden. Der Wert muss zwischen 0.0 (vollständig transparent) und 1.0 (vollständig opak) liegen. Dieser Wert ist standardmäßig 1.0 (vollständig opak).
Die Eigenschaft globalAlpha
kann nützlich sein, wenn Sie viele Formen auf dem Canvas mit ähnlicher Transparenz zeichnen möchten, ansonsten ist es im Allgemeinen nützlicher, die Transparenz beim Festlegen ihrer Farben auf einzelne Formen einzustellen.
Da die Eigenschaften strokeStyle
und fillStyle
CSS-RGB-Farbwerte akzeptieren, können wir die folgende Notation verwenden, um eine transparente Farbe zuzuweisen.
// Assigning transparent colors to stroke and fill style
ctx.strokeStyle = "rgb(255 0 0 / 50%)";
ctx.fillStyle = "rgb(255 0 0 / 50%)";
Die Funktion rgb()
hat einen optionalen zusätzlichen Parameter. Der letzte Parameter legt den Transparenzwert dieser bestimmten Farbe fest. Der gültige Bereich ist als Prozentsatz zwischen 0%
(vollständig transparent) und 100%
(vollständig opak) oder als Zahl zwischen 0.0
(entspricht 0%
) und 1.0
(entspricht 100%
) angegeben.
Ein globalAlpha
-Beispiel
In diesem Beispiel zeichnen wir einen Hintergrund aus vier unterschiedlich farbigen Quadraten. Darüber zeichnen wir eine Reihe halbtransparenter Kreise. Die Eigenschaft globalAlpha
ist auf 0,2
gesetzt, was für alle Formen ab diesem Punkt verwendet wird. Jeder Schritt in der for
Schleife zeichnet eine Reihe von Kreisen mit einem zunehmenden Radius. Das Endergebnis ist ein radialer Verlauf. Indem wir immer mehr Kreise übereinander legen, reduzieren wir effektiv die Transparenz der bereits gezeichneten Kreise. Durch Erhöhen der Schrittzahl und damit des Zeichnens von mehr Kreisen würde der Hintergrund vollständig aus der Mitte des Bildes verschwinden.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// draw background
ctx.fillStyle = "#FD0";
ctx.fillRect(0, 0, 75, 75);
ctx.fillStyle = "#6C0";
ctx.fillRect(75, 0, 75, 75);
ctx.fillStyle = "#09F";
ctx.fillRect(0, 75, 75, 75);
ctx.fillStyle = "#F30";
ctx.fillRect(75, 75, 75, 75);
ctx.fillStyle = "#FFF";
// set transparency value
ctx.globalAlpha = 0.2;
// Draw semi transparent circles
for (let i = 0; i < 7; i++) {
ctx.beginPath();
ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true);
ctx.fill();
}
}
Ein Beispiel mit rgb()
und Alphatransparenz
In diesem zweiten Beispiel machen wir etwas Ähnliches wie im vorherigen, aber anstatt Kreise übereinander zu zeichnen, habe ich kleine Rechtecke mit zunehmender Deckkraft gezeichnet. Durch die Verwendung von rgb()
haben Sie ein wenig mehr Kontrolle und Flexibilität, da wir den Füll- und Strichstil individuell festlegen können.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Draw background
ctx.fillStyle = "rgb(255 221 0)";
ctx.fillRect(0, 0, 150, 37.5);
ctx.fillStyle = "rgb(102 204 0)";
ctx.fillRect(0, 37.5, 150, 37.5);
ctx.fillStyle = "rgb(0 153 255)";
ctx.fillRect(0, 75, 150, 37.5);
ctx.fillStyle = "rgb(255 51 0)";
ctx.fillRect(0, 112.5, 150, 37.5);
// Draw semi transparent rectangles
for (let i = 0; i < 10; i++) {
ctx.fillStyle = `rgb(255 255 255 / ${(i + 1) / 10})`;
for (let j = 0; j < 4; j++) {
ctx.fillRect(5 + i * 14, 5 + j * 37.5, 14, 27.5);
}
}
}
Linienstile
Es gibt mehrere Eigenschaften, mit denen wir Linien gestalten können.
lineWidth = value
-
Legt die Breite der in Zukunft gezeichneten Linien fest.
lineCap = type
-
Legt das Aussehen der Enden von Linien fest.
lineJoin = type
-
Legt das Aussehen der "Ecken" fest, an denen sich Linien treffen.
miterLimit = value
-
Legt ein Limit am Gehrungswinkel fest, wenn zwei Linien in einem scharfen Winkel aufeinandertreffen, um zu steuern, wie dick die Verbindung wird.
getLineDash()
-
Gibt das aktuelle Strichmuster zurück, das ein Array von geraden Anzahl nicht-negativer Zahlen enthält.
setLineDash(segments)
-
Legt das aktuelle Strichmuster fest.
lineDashOffset = value
-
Gibt an, wo eine gestrichelte Linie beginnen soll.
Sie werden ein besseres Verständnis dafür bekommen, was diese Eigenschaften bewirken, indem Sie sich die Beispiele unten ansehen.
Ein lineWidth
-Beispiel
Diese Eigenschaft setzt die aktuelle Linienstärke. Werte müssen positive Zahlen sein. Standardmäßig ist dieser Wert auf 1,0 Einheiten eingestellt.
Die Linienbreite ist die Dicke des Strichs, der in der Mitte auf dem angegebenen Pfad verläuft. Mit anderen Worten, der gezeichnete Bereich erstreckt sich über die halbe Linienbreite auf beiden Seiten des Pfads. Da Canvas-Koordinaten sich nicht direkt auf Pixel beziehen, muss besondere Vorsicht angewendet werden, um scharfe horizontale und vertikale Linien zu erzielen.
Im Beispiel unten werden 10 gerade Linien mit zunehmender Linienbreite gezeichnet. Die Linie ganz links ist 1,0 Einheiten breit. Die ganz links und alle anderen ungeraden Linienbreiten erscheinen jedoch nicht scharf aufgrund der Positionierung des Pfads.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
for (let i = 0; i < 10; i++) {
ctx.lineWidth = 1 + i;
ctx.beginPath();
ctx.moveTo(5 + i * 14, 5);
ctx.lineTo(5 + i * 14, 140);
ctx.stroke();
}
}
Um scharfe Linien zu erhalten, muss man verstehen, wie Pfade gestrahlt werden. In den Abbildungen unten stellt das Raster das Canvas-Koordinatenraster dar. Die Quadrate zwischen den Gittern sind tatsächliche Bildschirm-Pixel. Im ersten Rasterbild unten wird ein Rechteck von (2,1) bis (5,5) gefüllt. Der gesamte Bereich dazwischen (hellrot) liegt auf den Pixelgrenzen, sodass das resultierende gefüllte Rechteck scharfe Kanten hat.
Wenn Sie einen Pfad von (3,1) bis (3,5) mit einer Linienstärke von 1,0
betrachten, enden Sie mit der im zweiten Bild dargestellten Situation. Der tatsächlich zu füllende Bereich (dunkelblau) erstreckt sich nur zur Hälfte in die Pixel auf beiden Seiten des Pfads. Eine Annäherung daran muss gerendert werden, was bedeutet, dass diese Pixel nur teilweise schattiert werden, und das Ergebnis ist, dass der gesamte Bereich (hellblau und dunkelblau) mit einer Farbe gefüllt wird, die nur halb so dunkel wie die tatsächliche Strichfarbe ist. Das passiert mit der 1.0
-Breitenlinie im vorherigen Beispielcode.
Um dies zu korrigieren, muss man bei der Erstellung des Pfads sehr genau sein. Wenn man weiß, dass eine 1,0
-Breitenlinie sich zur Hälfte zur einen oder anderen Seite des Pfads erstrecken wird, führt die Erstellung des Pfads von (3,5,1) bis (3,5,5) zu der im dritten Bild dargestellten Situation—die 1.0
-Breitenlinie füllt letztendlich eine einzelne vertikale Pixel-Linie vollständig und präzise aus.
Hinweis: Beachten Sie, dass in unserem vertikalen Linienbeispiel die Y-Position immer noch auf eine ganze Gitternetzlinienposition bezogen ist—wenn dem nicht so wäre, würden wir Pixel mit halber Abdeckung an den Endpunkten sehen (beachten Sie jedoch auch, dass dieses Verhalten von dem aktuellen lineCap
-Stil abhängt, dessen Standardwert butt
ist; Sie möchten möglicherweise konsistente Striche mit halben Pixelkoordinaten für ungerade Linienbreiten berechnen, indem Sie den lineCap
-Stil auf square
setzen, damit die äußere Grenze des Strichs um den Endpunkt herum automatisch verlängert wird, um das gesamte Pixel genau abzudecken).
Beachten Sie auch, dass nur die Start- und Endpunkte eines Pfads beeinflusst werden: wenn ein Pfad mit closePath()
geschlossen wird, gibt es keinen Start- und Endpunkt; stattdessen werden alle Endpunkte im Pfad unter Verwendung der aktuellen Einstellung des lineJoin
-Stils, dessen Standardwert miter
ist, mit ihrem verbundenen vorherigen und nächsten Segment verbunden, mit dem Effekt, die äußeren Grenzen der verbundenen Segmente automatisch zu ihrem Schnittpunkt zu verlängern, sodass der gerenderte Strich genau ganze Pixel abdeckt, die an jedem Endpunkt zentriert sind, wenn diese verbundenen Segmente horizontal und/oder vertikal sind. Sehen Sie sich die nächsten beiden Abschnitte an, um Demonstrationen dieser zusätzlichen Linienstile zu sehen.
Für Linien mit geraden Breiten endet jede Hälfte in einer ganzzahligen Anzahl von Pixeln, so dass Sie einen Pfad möchten, der zwischen den Pixeln liegt (das heißt, (3,1) bis (3,5)), anstatt in der Mitte der Pixel.
Während es anfänglich bei der Arbeit mit skalierbaren 2D-Grafiken etwas mühsam ist, den Pixelraster und die Position der Pfade zu beachten, stellt dies sicher, dass Ihre Zeichnungen unabhängig von Skalierung oder anderen beteiligten Transformationen korrekt aussehen. Eine an der richtigen Stelle gezeichnete Linie mit einer Breite von 1,0 wird bei einer Vergrößerung um den Faktor 2 zu einer scharfen 2-Pixel-Linie und erscheint an der richtigen Stelle.
Ein lineCap
-Beispiel
Die lineCap
-Eigenschaft bestimmt, wie die Endpunkte jeder Linie gezeichnet werden. Es gibt drei mögliche Werte für diese Eigenschaft, nämlich butt
, round
und square
. Standardmäßig ist diese Eigenschaft auf butt
eingestellt:
butt
-
Die Enden von Linien werden an den Endpunkten abgeschnitten.
round
-
Die Enden von Linien sind abgerundet.
square
-
Die Enden von Linien werden durch Hinzufügen eines Kästchens mit gleicher Breite und halber Höhe der Linienbreite abgeschlossen.
In diesem Beispiel zeichnen wir drei Linien, jede mit einem anderen Wert für die lineCap
-Eigenschaft. Ich habe auch zwei Führungslinien hinzugefügt, um die genauen Unterschiede zwischen den dreien zu sehen. Jede dieser Linien beginnt und endet genau auf diesen Führungslinien.
Die Linie links verwendet die Standardoption butt
. Sie werden feststellen, dass sie vollständig bündig mit den Führungslinien gezeichnet wird. Die zweite ist auf die Option round
eingestellt. Dies fügt ein Halbkreis an das Ende hinzu, der einen Radius von der halben Breite der Linie hat. Die rechte Linie verwendet die Option square
. Dies fügt ein Kästchen mit gleicher Breite und halber Höhe der Linienstärke hinzu.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Draw guides
ctx.strokeStyle = "#09f";
ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(140, 10);
ctx.moveTo(10, 140);
ctx.lineTo(140, 140);
ctx.stroke();
// Draw lines
ctx.strokeStyle = "black";
["butt", "round", "square"].forEach((lineCap, i) => {
ctx.lineWidth = 15;
ctx.lineCap = lineCap;
ctx.beginPath();
ctx.moveTo(25 + i * 50, 10);
ctx.lineTo(25 + i * 50, 140);
ctx.stroke();
});
}
Ein lineJoin
-Beispiel
Die lineJoin
-Eigenschaft bestimmt, wie zwei Verbindungssegmente (von Linien, Kreisbögen oder Kurven) mit nicht-nulliger Länge in einer Form miteinander verbunden werden (degenerierte Segmente mit null Länge, deren angegebene Endpunkte und Kontrollpunkte genau an derselben Position sind, werden übersprungen).
Es gibt drei mögliche Werte für diese Eigenschaft: round
, bevel
und miter
. Standardmäßig ist diese Eigenschaft auf miter
eingestellt. Beachten Sie, dass die lineJoin
-Einstellung keine Wirkung hat, wenn die beiden verbundenen Segmente in derselben Richtung verlaufen, da in diesem Fall kein Verbindungsbereich hinzugefügt wird:
round
-
Rundet die Ecken einer Form ab, indem ein zusätzlicher Kreisausschnitt in der Mitte am gemeinsamen Endpunkt der verbundenen Segmente gefüllt wird. Der Radius für diese abgerundeten Ecken entspricht der halben Linienbreite.
bevel
-
Füllt einen zusätzlichen dreieckigen Bereich zwischen dem gemeinsamen Endpunkt der verbundenen Segmente und den separaten äußeren rechteckigen Ecken jedes Segments.
miter
-
Verbundene Segmente werden verbunden, indem die Außenkanten ihrer Verbindung zu einem einzigen Punkt verlängert werden, mit dem Effekt, einen zusätzlichen rautenförmigen Bereich zu füllen. Diese Einstellung wird von der Eigenschaft
miterLimit
beeinflusst, die unten erläutert wird.
Das Beispiel unten zeichnet drei verschiedene Pfade und demonstriert jede dieser drei lineJoin
-Eigenschaftseinstellungen; das Ergebnis wird oben angezeigt.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.lineWidth = 10;
["round", "bevel", "miter"].forEach((lineJoin, i) => {
ctx.lineJoin = lineJoin;
ctx.beginPath();
ctx.moveTo(-5, 5 + i * 40);
ctx.lineTo(35, 45 + i * 40);
ctx.lineTo(75, 5 + i * 40);
ctx.lineTo(115, 45 + i * 40);
ctx.lineTo(155, 5 + i * 40);
ctx.stroke();
});
}
Eine Demo der miterLimit
-Eigenschaft
Wie Sie im vorherigen Beispiel gesehen haben, wenn zwei Linien mit der miter
-Option verbunden werden, werden die Außenkanten der beiden verbundenen Linien bis zu dem Punkt verlängert, an dem sie sich treffen. Für Linien, die in einem großen Winkel zueinander stehen, ist dieser Punkt nicht weit vom inneren Verbindungspunkt entfernt. Wenn die Winkel zwischen den Linien jedoch kleiner werden, nimmt die Entfernung (Miter-Länge) zwischen diesen Punkten exponentiell zu.
Die Eigenschaft miterLimit
bestimmt, wie weit der äußere Verbindungspunkt vom inneren Verbindungspunkt platziert werden kann. Wenn zwei Linien diesen Wert überschreiten, wird stattdessen eine Gehrung gezeichnet. Beachten Sie, dass die maximale Miter-Länge das Produkt der Linienbreite, gemessen im aktuellen Koordinatensystem, mit dem Wert dieser miterLimit
-Eigenschaft ist (deren Standardwert in der HTML <canvas>
10.0 beträgt), sodass das miterLimit
unabhängig von der aktuellen Anzeigenmaßstab oder beliebigen affinen Transformationen von Pfaden festgelegt werden kann: es beeinflusst nur die tatsächlich gerenderte Form von Linienkanten.
Genauer gesagt, das Miter-Limit ist das maximal zulässige Verhältnis der Verlängerungslänge (im HTML-Canvas wird es zwischen der äußeren Ecke der verbundenen Kanten der Linie und dem gemeinsamen Endpunkt der verbundenen Segmente gemessen, die im Pfad angegeben sind) zu halben Linienbreite. Es kann äquivalent als das maximal zulässige Verhältnis der Entfernung zwischen den inneren und äußeren Punkten der Verbindung der Kanten zur gesamten Linienbreite definiert werden. Es ist dann gleich dem Kotangens der Hälfte des minimalen inneren Winkels der verbundenen Segmente, unter dem keine Gehrungsverbindung gerendert wird, sondern nur eine Gehrungsverbindung:
miterLimit
= maxmiterLength
/lineWidth
= 1 / sin ( min θ / 2 )- Das Standard-Miter-Limit von 10,0 entfernt alle Miter für scharfe Winkel unter etwa 11 Grad.
- Ein Miter-Limit von √2 ≈ 1.4142136 (aufrundet) entfernt Miter für alle spitzen Winkel und behält Miterverbindungen nur für stumpfe oder rechte Winkel bei.
- Ein Miter-Limit von 1,0 ist gültig, schaltet jedoch alle Miter ab.
- Werte unter 1,0 sind ungültig für das Miter-Limit.
Hier ist eine kleine Demo, in der Sie miterLimit
dynamisch festlegen können, um die Auswirkungen auf die Formen auf dem Canvas zu sehen. Die blauen Linien zeigen, wo die Start- und Endpunkte für jede der Linien im Zick-Zack-Muster sind.
Wenn Sie in diesem Demo einen miterLimit
-Wert unter 4.2 angeben, wird keine der sichtbaren Ecken mit einer Miter-Erweiterung verbunden, sondern nur mit einer kleinen Gehrung in der Nähe der blauen Linien; bei einem miterLimit
über 10 sollten die meisten Ecken in diesem Demo mit einer Miter weit weg von den blauen Linien verbunden sein, und deren Höhe nimmt zwischen den Ecken von links nach rechts ab, da sie sich mit wachsenden Winkeln verbinden; mit Zwischenwerten werden die Ecken auf der linken Seite nur mit einem Gehrung nahe den blauen Linien verbunden, und die Ecken auf der rechten Seite mit einer Miter-Erweiterung (ebenfalls mit abnehmender Höhe).
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Clear canvas
ctx.clearRect(0, 0, 150, 150);
// Draw guides
ctx.strokeStyle = "#09f";
ctx.lineWidth = 2;
ctx.strokeRect(-5, 50, 160, 50);
// Set line styles
ctx.strokeStyle = "#000";
ctx.lineWidth = 10;
// check input
if (document.getElementById("miterLimit").checkValidity()) {
ctx.miterLimit = parseFloat(document.getElementById("miterLimit").value);
}
// Draw lines
ctx.beginPath();
ctx.moveTo(0, 100);
for (let i = 0; i < 24; i++) {
const dy = i % 2 === 0 ? 25 : -25;
ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
}
ctx.stroke();
return false;
}
Verwendung von Strichmustern
Die setLineDash
-Methode und die lineDashOffset
-Eigenschaft spezifizieren das Strichmuster für Linien. Die setLineDash
-Methode akzeptiert eine Liste von Zahlen, die die Abstände zum abwechselnden Zeichnen einer Linie und eines Lückenmusters angeben, und die lineDashOffset
-Eigenschaft gibt einen Versatz an, wo das Muster beginnen soll.
In diesem Beispiel erzeugen wir einen Effekt von "laufenden Ameisen". Es handelt sich um eine Animationstechnik, die häufig in Auswahlwerkzeugen von Grafikprogrammen zu finden ist. Es hilft dem Benutzer, den Auswahlrahmen vom Hintergrund zu unterscheiden, indem er den Rand animiert. Zu einem späteren Zeitpunkt dieser Anleitung erfahren Sie, wie Sie dies und andere einfache Animationen durchführen können.
const ctx = document.getElementById("canvas").getContext("2d");
let offset = 0;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.setLineDash([4, 2]);
ctx.lineDashOffset = -offset;
ctx.strokeRect(10, 10, 100, 100);
}
function march() {
offset++;
if (offset > 5) {
offset = 0;
}
draw();
setTimeout(march, 20);
}
march();
Verläufe
Genau wie bei einem normalen Zeichenprogramm können wir Formen mit linearen, radialen und konischen Verläufen füllen und umranden. Wir erstellen ein CanvasGradient
Objekt, indem wir eine der folgenden Methoden verwenden. Wir können dann dieses Objekt den Eigenschaften fillStyle
oder strokeStyle
zuweisen.
createLinearGradient(x1, y1, x2, y2)
-
Erstellt ein lineares Verlauf-Objekt mit einem Startpunkt von (
x1
,y1
) und einem Endpunkt von (x2
,y2
). createRadialGradient(x1, y1, r1, x2, y2, r2)
-
Erstellt einen radialen Verlauf. Die Parameter repräsentieren zwei Kreise, einen mit seinem Zentrum bei (
x1
,y1
) und einem Radius vonr1
, und den anderen mit seinem Zentrum bei (x2
,y2
) mit einem Radius vonr2
. createConicGradient(angle, x, y)
-
Erstellt ein konisches Verlauf-Objekt mit einem Startwinkel von
angle
in Radians, an der Position (x
,y
).
Zum Beispiel:
const lineargradient = ctx.createLinearGradient(0, 0, 150, 150);
const radialgradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 100);
Nachdem wir ein CanvasGradient
-Objekt erstellt haben, können wir ihm mit der Methode addColorStop()
Farben zuweisen.
gradient.addColorStop(position, color)
-
Erstellt einen neuen Farbstopp auf dem
gradient
Objekt. Dieposition
ist eine Zahl zwischen 0,0 und 1,0 und definiert die relative Position der Farbe im Verlauf, und dascolor
Argument muss ein String sein, der eine CSS<color>
darstellt, die die Farbe angibt, die der Verlauf an dieser Stelle des Übergangs erreichen soll.
Sie können so viele Farbstopps zu einem Verlauf hinzufügen, wie Sie benötigen. Im Folgenden ist ein sehr einfacher linearer Verlauf von Weiß zu Schwarz dargestellt.
const lineargradient = ctx.createLinearGradient(0, 0, 150, 150);
lineargradient.addColorStop(0, "white");
lineargradient.addColorStop(1, "black");
Ein createLinearGradient
-Beispiel
In diesem Beispiel erstellen wir zwei verschiedene Verläufe. Wie Sie hier sehen können, können sowohl die strokeStyle
als auch die fillStyle
Eigenschaften ein canvasGradient
Objekt als gültigen Eingang akzeptieren.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Create gradients
const linGrad = ctx.createLinearGradient(0, 0, 0, 150);
linGrad.addColorStop(0, "#00ABEB");
linGrad.addColorStop(0.5, "#fff");
linGrad.addColorStop(0.5, "#26C000");
linGrad.addColorStop(1, "#fff");
const linGrad2 = ctx.createLinearGradient(0, 50, 0, 95);
linGrad2.addColorStop(0.5, "#000");
linGrad2.addColorStop(1, "rgb(0 0 0 / 0%)");
// assign gradients to fill and stroke styles
ctx.fillStyle = linGrad;
ctx.strokeStyle = linGrad2;
// draw shapes
ctx.fillRect(10, 10, 130, 130);
ctx.strokeRect(50, 50, 50, 50);
}
Der erste ist ein Hintergrundverlauf. Wie Sie sehen können, haben wir zwei Farben an derselben Position zugewiesen. Dies tun Sie, um sehr scharfe Farbwechsel zu machen – in diesem Fall von Weiß zu Grün. Normalerweise spielt es keine Rolle, in welcher Reihenfolge Sie die Farbstopps definieren, aber in diesem speziellen Fall macht es einen bedeutenden Unterschied. Wenn Sie die Zuweisungen in der Reihenfolge behalten, in der Sie sie erscheinen lassen möchten, wird dies kein Problem darstellen.
Im zweiten Verlauf haben wir die Startfarbe (an Position 0,0) nicht zugewiesen, da es nicht unbedingt notwendig war, da sie automatisch die Farbe des nächsten Farbstopps annimmt. Daher macht das Zuweisen der schwarzen Farbe an Position 0,5 automatisch den Verlauf von Anfang bis zu diesem Stopp schwarz.
Ein createRadialGradient
-Beispiel
In diesem Beispiel werden wir vier verschiedene radiale Verläufe definieren. Da wir die Kontrolle über die Start- und Endpunkte des Verlaufs haben, können wir komplexere Effekte erzielen als üblicherweise bei den "klassischen" radialen Verläufen, die wir zum Beispiel in Photoshop sehen (das heißt, ein Verlauf mit einem einzigen Mittelpunkt, von dem aus der Verlauf kreisförmig nach außen expandiert).
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Create gradients
const radGrad = ctx.createRadialGradient(45, 45, 10, 52, 50, 30);
radGrad.addColorStop(0, "#A7D30C");
radGrad.addColorStop(0.9, "#019F62");
radGrad.addColorStop(1, "rgb(1 159 98 / 0%)");
const radGrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
radGrad2.addColorStop(0, "#FF5F98");
radGrad2.addColorStop(0.75, "#FF0188");
radGrad2.addColorStop(1, "rgb(255 1 136 / 0%)");
const radGrad3 = ctx.createRadialGradient(95, 15, 15, 102, 20, 40);
radGrad3.addColorStop(0, "#00C9FF");
radGrad3.addColorStop(0.8, "#00B5E2");
radGrad3.addColorStop(1, "rgb(0 201 255 / 0%)");
const radGrad4 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90);
radGrad4.addColorStop(0, "#F4F201");
radGrad4.addColorStop(0.8, "#E4C700");
radGrad4.addColorStop(1, "rgb(228 199 0 / 0%)");
// draw shapes
ctx.fillStyle = radGrad4;
ctx.fillRect(0, 0, 150, 150);
ctx.fillStyle = radGrad3;
ctx.fillRect(0, 0, 150, 150);
ctx.fillStyle = radGrad2;
ctx.fillRect(0, 0, 150, 150);
ctx.fillStyle = radGrad;
ctx.fillRect(0, 0, 150, 150);
}
In diesem Fall haben wir den Startpunkt leicht vom Endpunkt versetzt, um einen sphärischen 3D-Effekt zu erzielen. Es ist am besten zu vermeiden, dass sich die inneren und äußeren Kreise überlappen, da dies zu seltsamen Effekten führt, die schwer vorhersehbar sind.
Der letzte Farbstopp in jedem der vier Verläufe verwendet eine vollständig transparente Farbe. Wenn Sie einen schönen Übergang von diesem zum vorherigen Farbstopp haben möchten, sollten beide Farben gleich sein. Dies ist nicht offensichtlich aus dem Code, da er zwei verschiedene CSS-Farbmethoden als Demonstration verwendet, aber im ersten Verlauf #019F62 = rgb(1 159 98 / 100%)
.
Ein createConicGradient
-Beispiel
In diesem Beispiel definieren wir zwei verschiedene konische Verläufe. Ein konischer Verlauf unterscheidet sich von einem radialen Verlauf dadurch, dass er sich nicht um Kreise handelt, sondern um einen Punkt herumkreist.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// Create gradients
const conicGrad1 = ctx.createConicGradient(2, 62, 75);
conicGrad1.addColorStop(0, "#A7D30C");
conicGrad1.addColorStop(1, "#fff");
const conicGrad2 = ctx.createConicGradient(0, 187, 75);
// we multiply our values by Math.PI/180 to convert degrees to radians
conicGrad2.addColorStop(0, "black");
conicGrad2.addColorStop(0.25, "black");
conicGrad2.addColorStop(0.25, "white");
conicGrad2.addColorStop(0.5, "white");
conicGrad2.addColorStop(0.5, "black");
conicGrad2.addColorStop(0.75, "black");
conicGrad2.addColorStop(0.75, "white");
conicGrad2.addColorStop(1, "white");
// draw shapes
ctx.fillStyle = conicGrad1;
ctx.fillRect(12, 25, 100, 100);
ctx.fillStyle = conicGrad2;
ctx.fillRect(137, 25, 100, 100);
}
Der erste Verlauf ist in der Mitte des ersten Rechtecks positioniert und verschiebt einen grünen Farbstopp am Anfang zu einem weißem am Ende. Der Winkel beginnt bei 2 Radians, was dadurch auffällt, dass die Start-/Endlinie nach Südosten zeigt.
Der zweite Verlauf ist ebenfalls in der Mitte seines zweiten Rechtecks positioniert. Dieser hat mehrere Farbstopps, die bei jedem Viertel der Drehung von Schwarz zu Weiß wechseln. Das gibt uns den karierten Effekt.
Muster
In einem der Beispiele auf der vorherigen Seite haben wir eine Reihe von Schleifen verwendet, um ein Muster von Bildern zu erstellen. Es gibt jedoch eine viel einfachere Methode: die Methode createPattern()
.
createPattern(image, type)
-
Erstellt und gibt ein neues Canvas-Musterobjekt zurück.
image
ist die Quelle des Bildes (das heißt, einHTMLImageElement
, einSVGImageElement
, ein anderesHTMLCanvasElement
oder einOffscreenCanvas
, einHTMLVideoElement
oder einVideoFrame
, oder einImageBitmap
).type
ist ein String, der angibt, wie das Bild verwendet werden soll.
Der Typ gibt an, wie das Bild verwendet werden soll, um das Muster zu erstellen, und muss einer der folgenden String-Werte sein:
repeat
-
Kachelt das Bild sowohl in vertikaler als auch in horizontaler Richtung.
repeat-x
-
Kachelt das Bild horizontal, aber nicht vertikal.
repeat-y
-
Kachelt das Bild vertikal, aber nicht horizontal.
no-repeat
-
Kachelt das Bild nicht. Es wird nur einmal verwendet.
Wir verwenden diese Methode, um ein CanvasPattern
Objekt zu erstellen, das den Verlaufsmethoden, die wir oben gesehen haben, sehr ähnlich ist. Sobald wir ein Muster erstellt haben, können wir es den Eigenschaften fillStyle
oder strokeStyle
zuweisen. Zum Beispiel:
const img = new Image();
img.src = "some-image.png";
const pattern = ctx.createPattern(img, "repeat");
Hinweis: Wie bei der Methode drawImage()
müssen Sie sicherstellen, dass das verwendete Bild geladen ist, bevor Sie diese Methode aufrufen, da das Muster ansonsten möglicherweise falsch gezeichnet wird.
Ein createPattern
-Beispiel
In diesem letzten Beispiel erstellen wir ein Muster, das der fillStyle
-Eigenschaft zugewiesen wird. Erwähnenswert ist die Verwendung des onload
-Handlers des Bildes. Dies soll sicherstellen, dass das Bild geladen ist, bevor es dem Muster zugewiesen wird.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
// create new image object to use as pattern
const img = new Image();
img.src = "canvas_create_pattern.png";
img.onload = () => {
// create pattern
const pattern = ctx.createPattern(img, "repeat");
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 150, 150);
};
}
Schatten
Die Verwendung von Schatten umfasst nur vier Eigenschaften:
shadowOffsetX = float
-
Gibt die horizontale Entfernung an, die der Schatten vom Objekt erstrecken soll. Dieser Wert wird nicht von der Transformationsmatrix beeinflusst. Der Standardwert ist 0.
shadowOffsetY = float
-
Gibt die vertikale Entfernung an, die der Schatten vom Objekt erstrecken soll. Dieser Wert wird nicht von der Transformationsmatrix beeinflusst. Der Standardwert ist 0.
shadowBlur = float
-
Gibt die Größe des Unschärfeeffekts an; Dieser Wert entspricht keiner Anzahl von Pixeln und wird nicht von der aktuellen Transformationsmatrix beeinflusst. Der Standardwert ist 0.
shadowColor = color
-
Ein Standard-CSS-Farbwert, der die Farbe des Schatteneffekts angibt; Standardmäßig ist es volltransparentes Schwarz.
Die Eigenschaften shadowOffsetX
und shadowOffsetY
geben an, wie weit der Schatten in den X- und Y-Richtungen vom Objekt erstreckt werden soll; Diese Werte werden nicht von der aktuellen Transformationsmatrix beeinflusst. Verwenden Sie negative Werte, um den Schatten nach oben oder nach links zu erstrecken, und positive Werte, um den Schatten nach unten oder nach rechts zu erstrecken. Standardmäßig sind beide 0.
Die Eigenschaft shadowBlur
gibt die Größe des Unschärfeeffekts an; Dieser Wert entspricht keiner Anzahl von Pixeln und wird nicht von der aktuellen Transformationsmatrix beeinflusst. Der Standardwert ist 0.
Die Eigenschaft shadowColor
ist ein Standard-CSS-Farbwert, der die Farbe des Schatteneffekts angibt; Standardmäßig ist sie volltransparentes Schwarz.
Hinweis: Schatten werden nur für source-over
Kompositionsoperationen gezeichnet.
Ein schattiertes Textbeispiel
Dieses Beispiel zeichnet eine Textzeichenfolge mit einem Schatteneffekt.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.shadowColor = "rgb(0 0 0 / 50%)";
ctx.font = "20px Times New Roman";
ctx.fillStyle = "Black";
ctx.fillText("Sample String", 5, 30);
}
Wir werden uns die font
-Eigenschaft und die Methode fillText
im nächsten Kapitel über Zeichnen von Text ansehen.
Canvas-Füllregeln
Beim Verwenden von fill
(oder clip
und isPointInPath
) können Sie optional einen Füllregelalgorithmus angeben, um zu bestimmen, ob ein Punkt innerhalb oder außerhalb eines Pfads liegt und ob er daher gefüllt wird oder nicht. Dies ist nützlich, wenn ein Pfad sich selbst schneidet oder verschachtelt ist.
Zwei Werte sind möglich:
nonzero
-
Die Nicht-Null-Aufregel, die die Standardregel ist.
evenodd
In diesem Beispiel verwenden wir die evenodd
-Regel.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.beginPath();
ctx.arc(50, 50, 30, 0, Math.PI * 2, true);
ctx.arc(50, 50, 15, 0, Math.PI * 2, true);
ctx.fill("evenodd");
}