戴兜

戴兜的小屋

Coding the world.
github
bilibili
twitter

HTML5 Canvas Study Notes

image

As a front-end novice, I have always been hesitant to touch Canvas. Recently, I have done some superficial learning about Canvas operations, which has fulfilled a personal wish. I have simply organized some of my notes and learning insights.

The goal is to create a basic animation scene of Flappy Bird.

Using canvas can be really addictive (fog)

Part.1 Preparation#

I prepared some small materials for creating this simple animation in advance, as follows:

Bird: https://i.loli.net/2019/08/14/yRFjQEdoSpx9YDa.png

Ground: https://i.loli.net/2019/08/14/QyhMSrTwul2H7A9.png

Sky (background): https://i.loli.net/2019/08/14/YHtj95f2MxOduKh.png

Part.2 Start#

PS: I feel a better approach would be to draw the background and ground using two separate Canvases, but in this article, all animation elements will be drawn on the same Canvas.

First, create a Canvas (Note: using CSS to modify the canvas may cause distortion, so try to define the canvas dimensions using the height and width attributes)

Next, use its own getContext() to get the Canvas context

Part.2-1 Load Image Resources#

There are many ways to load images, and I used the method below. I don't know how it's generally done, but this method is very convenient for maintenance and development.

At this point, the three image resources we need have all been loaded, and it's very simple to use them: images["bg"], images["bird"], images["ground"] can be used to retrieve the corresponding images.

Part.3 Draw Background#

This is the simplest part. Before drawing, let's understand the drawImage() method of Canvas.

What each parameter corresponds to can be understood with a picture from MDN. For more details, MDN has a more detailed description, which I won't elaborate on here.

image

Now drawing the background is no longer a problem for us. Let's supplement the previous draw function.

Part.4 Draw Ground#

We will also draw the ground image into the Canvas using the same method. To limit the height of the ground to 80px, we need to scale it proportionally when drawing. (A better solution would be to preprocess the image to avoid scaling, but I’m lazy)

Then… you will find that the width of the ground is far from enough. How can we tile the image? The answer is: loop.

Estimate a value that can fill the entire ground. (To avoid exposing the animation, you can give a little more)

The background and ground have been drawn, so how do we add a backward movement effect to the ground?

Part.4-1 Frame Update#

We might first think of using setTimeout() or setInterval(), but for Canvas, the browser's window global object provides requestAnimationFrame() to update frame content more efficiently.

Why use requestAnimationFrame() instead of setTimeout() or setInterval()?

requestAnimationFrame() can more accurately keep the frame rate around 60fps, avoiding excessive drawing or animation stuttering.

requestAnimationFrame() automatically stops drawing when elements are not visible, the browser is in the background, or the tab is not active, saving performance overhead. Compared to setTimeout() or setInterval(), requestAnimationFrame() is optimized by the browser for its calling timing, making it more efficient and reliable.

Modify run() and draw() according to our needs, and don't forget to use clearRect() at the beginning of draw() to clear the Canvas.

Now the ground cannot move yet. To make the ground move, we need to change the position of the entire ground in each frame. The possibility of the ground moving infinitely backward is very small because we only drew 22 widths of the ground in this demo. However, we can simulate the illusion of the ground moving infinitely backward.

The simplest method is to let the ground move back 1/3 of a width (referring to one of the 22 parts) for the first three frames of each group, and move forward 1 width in the fourth frame of each group. This way, we can create the illusion of the ground moving infinitely backward.

Part.4-2 Implementing Movement#

So, how do we make these 22 pieces of "ground" move backward simultaneously? The first method is to directly modify the ground's x-axis coordinate each time we draw. The second method is to move the coordinate system before drawing the ground each time. We will use the second method.

This involves saving and restoring the rendering context's drawing state, corresponding to save() and restore(). According to MDN, the properties that can be saved include: strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, lineDashOffset, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation, font, textAlign, textBaseline, direction, imageSmoothingEnabled.

We use translate() to move the ground coordinate system.

Well, of course, a speed of 1/3 grid/frame is too fast for the ground. After fine-tuning the speed, the drawing part of the code is supplemented as follows:

Part.5 Draw Bird Animation#

The bird animation has 3 different states, which are processed into a sprite sheet stored in a PNG image. With the experience from before, handling this bird becomes much simpler. Remember the sx/sy/sWidth/sHeight parameters mentioned earlier regarding the drawImage() method? These four parameters are used to segment each frame of the image.

Don't forget to limit the bird's flapping rate, otherwise it will be mysteriously chaotic...

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.