Ok, hier ist eine Frage: "Warum brauchen wir dafür einen Artikel, Nash?"
Nehmen Sie Platz.
Nein, warte! Schauen Sie sich das zuerst an.

Genau. Was war das?
drawImage
ist die Methode, mit der ein Bild angezeigt oder „gezeichnet“ wird canvas
. Möglicherweise wissen Sie bereits, dass es nicht so einfach ist, nur die URI des Bildes an das Bild zu übergeben. drawImage
akzeptiert maximal 9 Parameter. Sie gehen so etwas, bereit? Halt deinen Atem an…
(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
Ausatmen.
Ich fand die Dokumentation drawImage
etwas verwirrend und hardcore. Nur die Dokumentation, ja. Das Konzept und die Funktionsweise der API eignen sich hervorragend für alle Anforderungen, denen sie dienen soll.
Wir werden die oben genannten Parameter nacheinander auf eine Weise durchgehen, die für Sie völlig sinnvoll ist. Wenn Sie zu irgendeinem Zeitpunkt im Artikel sagen: „Ich wollte nur ein Bild auf meine Leinwand zeichnen, lieber Nash. Warum meine Gedanken durch den Wecker bringen? “, Wird es eine verständliche Frustration sein.
Die Art und Weise, wie drawImage
funktioniert, scheint in gewissem Maße komplex zu sein, aber diese Komplexität macht sie drawImage
immens mächtig und nützlich - wie wir am Ende anhand von Beispielen sehen werden. Darüber hinaus ist die Komplexität nur oberflächlich: Wenn Sie das ganze Bild verstanden haben, ist es eine Downhill-Radtour auf einer Landstraße irgendwo in Europa.
Am Ende dieses Artikels können Sie anhand der Werte der 9 Parameter visualisieren, wie drawImage
ein bestimmtes Bild canvas
gezeichnet wird. Klingt nach einer Supermacht, die Sie vielleicht haben möchten? Okay, dann lass uns gleich eintauchen!
Laden eines Bildes in Leinwand
Beginnen wir einfach mit einem Bild und einem HTML5 canvas
.
So sieht unser Verzeichnis aus

In unserer index.html
Datei haben wir so ein neues Canvas-Element erstellt.
Unser Ziel ist es, das cat.jpg
Bild aufzunehmen und auf die Leinwand zu bringen ( #my-canvas
). Und wie ich schon sagte, es ist nicht so einfach, Betty! Sonst würde ich diesen Artikel nicht schreiben, fühlst du mich? Gut.
Lassen Sie uns zunächst das canvas
Element mit JavaScript ausrichten und seinen Kontext abrufen.
const myCanvas = document.getElementById('my-canvas'); const myContext = myCanvas.getContext('2d');
Wir müssen myContext
mit dem canvas
Element interagieren . Wenn canvas
es sich um ein leeres Blatt Papier handelt, ist der Kontext der Leinwand der Stift. Intuitiv werden Sie Ihrem Stift sagen, er soll etwas auf ein leeres Blatt Papier zeichnen und nicht nur das Papier anschreien, um etwas auf sich selbst zu zeichnen, oder?

Es gibt eine Reihe von Dingen, mit denen Sie arbeiten können context
. Sie können ein Rechteck, eine Ellipse, eine Linie oder ein Bild zeichnen . Beachten Sie außerdem, dass der Kontext myContext
implizit mit verknüpft ist myCanvas
. Sie können mehrere canvas
es haben und getContext()
jeden von ihnen aufrufen , um einen neuen Kontext / Stift für jeden zu erhalten. In unserem Fall handelt es sich nur um eine Zeichenfläche ( myCanvas
) und nur einen Kontext ( myContext
).
Okay, wenn das aus dem Weg ist, können wir endlich anfangen, unsere Füße nass zu machen drawImage
.

Zur Auffrischung sind hier die 9 Parameter, die drawImage
akzeptiert werden.
(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
Wir beginnen mit dem ersten Parameter image
. Schreiben wir zuerst etwas, das nicht funktioniert.
context.drawImage('./cat.jpg', 0, 0);
Sehen Sie die beiden Nullen am Ende? Gut. Dies ist nicht der Teil des Artikels, in dem Sie verstehen sollen, wofür sie da sind. Ignoriere sie jetzt, halte einfach im Hinterkopf, dass Nash 2 Nullen geschrieben und sie nicht erklärt hat. Ich werde nichts dagegen haben.
Beachten Sie nun ...('./cat.jpg',..
in der obigen Codezeile. Scheint eine vollkommen korrekte URI zu sein, nicht wahr? Und es ist ... aber wenn Sie index.html
in einem Browser starten, wird eine lange, lange Fehlermeldung angezeigt, die mit der unten gezeigten identisch ist.
ERROR: The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)
*Schluck*
Der Fehler sagt uns, dass es ein Bildelement und nicht nur eine URI für das Bild benötigt. Um das zu umgehen, können wir dies tun.
const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); const img = new Image(); img.src = './cat.jpg'; img.onload = () => { context.drawImage(img, 0, 0); };
Das haben Sie nicht erwartet, oder? Canvas benötigt ein vorinstalliertes Bild, um es in sich selbst zeichnen / anzeigen zu können. Übrigens muss man keine Verachtung gegenüber der Leinwand zeigen. Es hat seinen Grund, es ist genau wie der Rest von uns. Wir werden irgendwann sehen, was diese Gründe sind, und vielleicht können Sie dann mitfühlen.

Um es noch einmal zusammenzufassen:
drawImage
fragt nach 9 Parametern, von denen der erste ist image
. Wir haben nachgesehen und verstanden, dass canvas
zum Zeichnen ein vorinstalliertes Bild erforderlich ist und nicht nur eine URI für das Bild. Warum braucht es das? Es wird klar, wenn Sie lesen.
Jetzt ist es Zeit für die verbleibenden 8 Parameter. Pop deine Kragen! Ich werde dir zuerst etwas Grafikbearbeitung beibringen!
So beschneiden Sie ein Bild
Jedes einzelne Grafikbearbeitungsprogramm, auch das einfachste, verfügt über die Funktion zum Zuschneiden. Es ist ziemlich einfach: Öffnen Sie ein Bild> wählen Sie den Bereich aus, den Sie sehen möchten> klicken Sie auf Zuschneiden. Und einfach so ist der nackte Bierbauch dieses widerlich riechenden alten Mannes raus. Poof!

Technologie! Speichern der Instagramme von Personen, seit es Instagram gibt.
Machen wir einen Schritt zurück und hören hier auf.

Lassen Sie uns einige Punkte darauf markieren.

"Einen Augenblick! sx
, sy
, sWidth
Und sHeight
? Ich habe sie schon einmal gesehen! "
Ja, vor ungefähr einer Minute! Was uns zum fleischigsten Teil des Artikels führt.
Anzeigen eines Bildes auf Leinwand, Schritt 1: Auswahl
The first task drawImage
performs (behind the scenes) is it selects a portion of the image based on the four s
parameters (sx, sy, sWidth, sHeight
). You can say that s in all the s parameters stands for “select”.
Here’s how it goes. sx
and sy
mark the point on the image from where the selection is to begin, or in other words the coordinates of the top left corner of the selection rectangle. sWidth
and sHeight
then, are the width and height of the selection rectangle respectively. You can scroll right up to the last image to get a clearer picture of what I am trying to explain.
“But why the selection Nash? Can’t it just display the entire image?” We’re getting closer to all your answers, patience.
Just know that the first step drawImage
performs after receiving a proper image is it selects a portion/area of the image based on the s parameters (sx, sy, sWidth, sHeight)
you provide.

Remember that you don’t always have to select a small portion of the image, you can select the entire image if you want to. In that case sx
and sy
will have values 0 and 0 respectively and sWidth
, sHeight
will be the same as the image’s width and height.
Also, negative values are welcome for sx
and sy
. The values of sx
and sy
are relative to the origin of the image on the top left.

Once drawImage
has selected the area of image you asked it to – and we’ll see soon why selecting an area of the image helps – the next step is to draw the selected portion of the image on the canvas.
“Originally” s
and d
in the official documentation stand for ‘source’ and ‘destination’. But, just between us, let’s call it ‘select’ and ‘draw’. It makes much more sense this way, doesn’t it?
Again. s
election is done, the next step is to d
raw.
Displaying an image on canvas, Step 2: Drawing
To draw the selected portion of the image, we again need four parameters.
- x Coordinate of where to start drawing on the canvas. (
dx
) - y Coordinate of where to start drawing on the canvas. (
dy
) - How wide to draw the image. (
dWidth
) - How high/tall to draw the image. (
dHeight
)
The values of dx
and dy
will be relative to the origin of the canvas.

There’s a very important but subtle detail to notice here. dWidth
and dHeight
are in no way tied to sWidth
and sHeight
. They are independent values. Why do you need to know that? Well, because if you don’t choose values of the width and height of ‘draw’ carefully you will end up with a stretched or squashed image, like this.

So if that’s something you’re not looking for (which I hope you’re not), make sure to maintain the aspect ratio. Or so to say sWidth
divided by sHeight
should be equal to dWidth
divided by dHeight
. That was a small little disclaimer, you’re the ruler of your own world and free to choose whatever values you like.
The whole process of displaying/drawing an image on canvas can thus be summarised in just two steps: Selection and Drawing.

Awesome! Not so complicated after all is it?
Now at this point, we’re done with all the theory. In rest of the article that follows we’ll bake the batter of knowledge spread around your head with a fun and practical example and you’ll be good to go. But, before we do that, let’s talk about one last important thing concerning drawImage
.
The default values
Remember my lecture on “hey keep the aspect ratio and be careful don’t take chocolates from strangers…”? Well, as it turns out, you can omit certain values and not worry about the aspect ratio at all. As far as taking chocolates from strangers go, again — you’re the ruler of your own world.
Here’s one way to use the method.
drawImage(image, dx, dy)
That is all! In this case, you’re telling drawImage
only the location on canvas where to start the drawing. The rest, sx
, sy
, sWidth
, sHeight
, dWidth
and dHeight
are taken care of automagically. The method selects the entire image (sx = 0, sy = 0, sWidth = image's width, sHeight = images' height)
and starts drawing on canvas at (dx, dy)
with dWidth
and dHeight
same as sWidth
(image’s width), sHeight
(image’s height) .
Remember the two zeroes that I didn’t explain? That is where the two zeroes came from.
Yet another way to use the method is,
drawImage(image, dx, dy, dWidth, dHeight)
In this form sx, sy, sWidth and sHeight
are taken care of, and the method automatically selects the entire image and leaves it up to you to choose where and how large of an image to draw.
Pretty cool! isn’t it?
If I can have your attention for another two minutes I’d like to tell you why s
election and d
rawing are two separate operations. And how it is helpful.
Do I have your attention? Great!
So here.
Heard of sprites before? You see, sprites are a computer graphics concept where a graphic may be moved on-screen and otherwise manipulated as a single entity.
…?
I copied this definition from Google to sound suave.
Alright alright. Remember Mario?
Good.
Let’s do something fun.
Animating Mario with drawImage
You see, when Mario moves forward/backward or in any other direction, it appears as if he is walking. His position changes but also there is an accompanying animation of his legs and hands moving.
How do they do that? Do they show different images of Mario in succession, like a flipbook and it appears as if he’s moving?
Well, 50% yes. Imagine how resource intensive storing and loading a huge set of images describing every frame of animation in our program (or game) would be. Instead, there’s a single image and all the positions are laid out in a grid. Like the one shown below.

To execute an animation, instead of loading a new image every millisecond, a portion of the same image is shown through a viewport just at different positions. Clever isn’t it?
So yes, it’s sorta like a flipbook, a clever flipbook actually.
Now if you could just stretch a little and pop your knuckles I would like us to recreate Mario’s walking animation. We’ll use the sprite shown above and everything we have learnt about drawImage
so far.
Ready? Here we go!
Let’s take another look at our sprite and try to figure the grid dimensions that it has been laid out on.

All that we have done here is imagined a grid over the sprite. Notice that the entire grid is made up of cells of equal dimensions (32 x 39)
. But it’s still just one image, remember that.
Great! Now let’s get to writing some code. We’ll start in the usual way by first creating a canvas
element, grabbing it and its context in JavaScript, and then loading our Mario spritesheet.
// index.js const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const img = new Image(); img.src = './mario.png'; img.onload = () => { ctx.drawImage(img, 0, 0); };
// style.css canvas { /*Add a border around canvas for legibility*/ border: 1px solid grey; }
The above code will result in the following.

Woah-kay! We’ve got the image showing! What’s happening really?
Here, we’re using the form of drawImage
–drawImage(image, sx, sy)
–where the whole image is s
elected and d
rawn on the canvas as it is.

What we want to do, first of all, is select just one cell of the grid and draw just that single cell. Let’s start out by first making tweaks to our code that selects the first cell in the third row, the one in which Mario is standing facing east. We’ll figure how to animate once we have that done. Sounds good? Lovely!
Let’s make the necessary changes to our code.
const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d');
// Mario variables const MARIO_WIDTH = 32; const MARIO_HEIGHT = 39;
const mario = new Image(); mario.src = './mario.png'; mario.onload = () => { ctx.drawImage( // Image mario, // ---- Selection ---- 0, // sx MARIO_HEIGHT * 2, // sy MARIO_WIDTH, // sWidth MARIO_HEIGHT, // sHeight // ---- Drawing ---- 0, // dx 0, // dy MARIO_WIDTH, // dWidth MARIO_HEIGHT // dHeight ); };
First off, notice the two variables MARIO_WIDTH
and MARIO_HEIGHT
. They are the dimensions of the grid cell, that’s all they are. We defined them to make it easier for us to traverse the grid using just multiples of each of those constants. Makes sense?
Good.
Next, in the // Selection
block we defined the area of the image we want to select, in the // Drawing
section we defined the width and height and the position from where to start drawing on the canvas… aaand just like that we managed to draw just one cell of the entire imaginary grid.

Pretty simple, just selection and drawing. Now at this point I’d like to digress into an older topic about aspect ratio. “Nash! again? ugghh” I know I know. But it’s cool! Look!
If I change the values of dWidth
or dHeight
or both of them, look at how the image stretches and squashes.
... ctx.drawImage( // Image mario, // ---- Selection ---- 0, // sx MARIO_HEIGHT * 2, // sy MARIO_WIDTH, // sWidth MARIO_HEIGHT, // sHeight // ---- Drawing ---- 0, // dx 0, // dy MARIO_WIDTH * 2, // dWidth MARIO_HEIGHT * 1.5 // dHeight ); ...

Hah! See! That’s why I was advising you to maintain the aspect ratio and that the values of selection and drawing have no real interconnection.
Okay, back to what we were doing.
So now we have Mario in the canvas, small and little. We need to animate it, or in other words show different frames at the same location in succession and make the illusion of movement happen. Was I too specific? Heck yeah!
We can do that by selecting the grid cells we want to draw in succession. We just need to change the value of sx
by the multiples of MARIO_WIDTH
.
Now doing this will require the use of requestAnimationFrame
and I have been explaining that in a streak in this article and this article.
As a small challenge why don’t you go ahead and try accomplishing this on your own? In any case, you can check out this Codepen where I have Mario running like this. The pen has enough comments to help you understand the tiny bit of high school math that’s being used to make the animation happen.
Cute little thing!
Damit sind wir mit einer sehr umfassenden Erklärung fertig drawImage
. Hoffe es hat euch gefallen.
Wenn du es bis hierher geschafft hast, wie wäre es, wenn du mir ein Feedback oder #goodvibes auf Twitter schießt?
Dieser Artikel wurde ursprünglich auf www.nashvail.me veröffentlicht.Habe ich dir von meiner neuen Website erzählt? Und habe ich dir gesagt, dass es auch einen Newsletter gibt? Ich würde mich freuen, wenn Sie sich anmelden, damit ich Sie jedes Mal benachrichtigen kann, wenn ich etwas Neues veröffentliche oder etwas Neues in meinem Shop zum Verkauf stelle. Ich werde weiterhin Artikel auf Medium veröffentlichen, aber es wird eine zweiwöchige Pause zwischen dem ersten Erscheinen auf meiner Website und dem Erscheinen hier geben.
Vielen Dank für das Lesen und vielen Dank für Ihre Unterstützung. Hab einen guten! :) :)