canvas基本动画_使用html canvas和javascript的基本动画

本文翻译自《basic-animation-using-the-html-canvas-and-javascript》,介绍了如何利用HTML Canvas和JavaScript创建基本的动画效果。
摘要由CSDN通过智能技术生成

canvas基本动画

Javascript can do a lot of cool things. The introduction of the canvas element in HTML5 expanded what we can do with Javascript even further. So what is a canvas element? It’s an element that acts as a container for javascript drawn graphics. Although we won’t demonstrate this here, you can take your animations a step further by interacting with them using event listeners and handlers. We’ll instead make a simple animation with some different size circles bouncing around the screen.

JavaScript可以做很多很酷的事情。 HTML5中canvas元素的引入进一步扩展了我们对Javascript的处理能力。 那么画布元素是什么? 它是充当javascript绘制图形容器的元素。 尽管我们不会在此处进行演示,但是您可以通过使用事件侦听器和处理程序与动画进行交互来使动画更进一步。 相反,我们将制作一个简单的动画,其中一些大小不同的圆圈在屏幕周围反弹。

First let’s create and set up our canvas element in our index.html file:

首先,让我们在index.html文件中创建并设置我们的canvas元素:

index.html
....

<body>
<canvas></canvas>
<script src="canvas.js"></script>
</body>
....

The <html> tag of a document doesn’t quite take up the entire screen. Since we’re planning on making our animation full page, and we have access to the window element which does fill the entire screen, we’ll use javascript to access it’s innerHeight and innerWidth properties and set our canvas height and width to those values. This will ensure our canvas takes up the entire height and width of the browser window. Because the default margin value of the <body> tag isn’t 0, we’ll need to update that as well:

文档的<html>标记并不能完全占据整个屏幕。 由于我们计划将动画制作成全页,并且可以访问确实innerHeight整个屏幕的window元素,因此我们将使用javascript来访问它的innerHeightinnerWidth属性,并将画布的height和width设置为这些值。 这将确保我们的画布占据浏览器窗口的整个高度和宽度。 因为<body>标签的默认边距值不为0,所以我们也需要更新它:

canvas.jsconst canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const body = document.querySelector('body');
body.style.margin = 0;

Next we’ll create a c variable in our javascript file that will call the .getContext() method on our canvas and pass in 2d as an argument. This returns a drawing context on the canvas and essentially turns c into a super object that has access to all of the drawing properties and functions we need to draw 2d elements:

接下来,我们将在javascript文件中创建一个c变量,该变量将称为。 我们画布上的getContext()方法,并将2d作为参数传递。 这将在画布上返回一个绘图上下文,并且实质上将c变成一个超级对象,该对象可以访问我们绘制2d元素所需的所有绘制属性和功能:

canvas.jsconst c = canvas.getContext('2d')

A few key methods that we’ll be utilizing on c are .beginPath(), .strokeStyle(), and .stroke(). .beginPath() does just what it says, it begins a new path from which we can draw. The .strokeStyle() method is used to alter the color, gradient, or pattern of our objects, and the .stroke() method is what actually draws our object.

我们将在c上使用的一些关键方法是.beginPath() .strokeStyle().stroke().beginPath()就像它所说的那样,它开始了我们可以从中绘制的新路径。 .strokeStyle()方法用于更改对象的颜色,渐变或图案,而.stroke()方法是实际绘制对象的对象。

Let’s go ahead and create a circle. We can do this by first calling c.beginPath() to indicate that we wish to begin a new path, and then calling c.arc() . This method accepts arguments for the x coordinate for the center of the circle, the y coordinate for the center of the circle, the radius, the starting angle in radians, and the end angle in radians. It also takes an optional argument for counterclockwise. For the time being we’ll hard code values for x, y, and radius. Lastly we can give our circle a color and finally get it on the page by calling c.strokesStyle() and c.stroke():

让我们继续创建一个圆圈。 为此,我们可以先调用c.beginPath()来表示我们希望开始一条新路径,然后再调用c.arc() 。 此方法接受以下参数:圆心的x坐标,圆心的y坐标,半径,以弧度为单位的起始角度和以弧度为单位的终止角度。 它还为逆时针添加了一个可选参数。 目前,我们将对x,y和radius的值进行硬编码。 最后,我们可以给圆加一个颜色,最后通过调用c.strokesStyle()c.stroke()在页面上获取颜色:

canvas.jsc.beginPath();
c.arc(200, 200, 30, 0, Math.PI * 2, false );
c.strokeStyle = "pink";
c.stroke();

Now that we have our circle, let’s get it to actually do something. We’ll need a function, let’s call it animate, that calls requestAnimationFrame(). requestAnimationFrame() takes a function as an argument and was introduced as an alternative to the traditional way of creating animations which included recursively calling .setTimeout(). We’re going to pass in our newly created animate function as the argument for requestAnimateFrame(), which will essentially create an infinite loop. This is what we want since we’ll be altering the x and y coordinates of our circle slightly with each page repaint. This will give the illusion of our circle moving across the page:

现在我们有了圈子,让我们开始实际做点事情。 我们需要一个函数,称为animate ,它调用requestAnimationFrame()requestAnimationFrame()以函数作为参数,并作为传统的动画创建方法的替代方法引入,该方法包括递归调用.setTimeout() 。 我们将传入新创建的animate函数作为requestAnimateFrame()的参数,这实际上将创建一个无限循环。 这就是我们想要的,因为我们将在每次重新绘制页面时略微更改圆的x和y坐标。 这会给我们一个圈子在页面上移动的错觉:

canvas.jsconst animate = () => {
requestAnimationFrame(animate)
}animate()

Let’s now place the code we previously wrote to create a circle within animate. Right now this code is just repainting the circle in place since we haven’t actually updated the x and y coordinates:

现在,让我们放置之前编写的代码以在animate创建一个圆。 现在,由于我们尚未实际更新x和y坐标,因此这段代码只是在重新绘制圆上:

canvas.jsconst animate = () => {
requestAnimationFrame(animate);c.beginPath();
c.arc(200, 200, 30, 0, Math.PI * 2, false );
c.strokeStyle = "pink";
c.stroke();
}animate()

We want to change the x and y values of our circle on each refresh as this is what will give us the illusion of movement. First we’ll create variables for our circles x and y coordinates outside of our function. We’ll then increment them on every refresh by setting x and y to x += 1 and y += 1. We’ll also need to clear the canvas every time the page refreshes so that the images aren’t just drawn on top of each other. We do this by calling c.clearRect() which takes an x-axis coordinate of the canvas’s starting point, a y-axis coordinate of the canvas’s starting point, width of the canvas and its height as arguments. We want to clear the entire canvas, so we’ll set those values to 0, 0, innerWidth, innerHeight:

我们想在每次刷新时更改圆的x和y值,因为这会给我们带来运动的错觉。 首先,我们将在函数之外为圆的x和y坐标创建变量。 然后,通过将xy设置为x += 1y += 1来使它们在每次刷新时递增。 每次页面刷新时,我们还需要清除画布,以使图像不只是在彼此之上绘制。 我们通过调用c.clearRect()来做到这一点,该方法以画布起点的x轴坐标,画布起点的y轴坐标,画布的宽度及其高度作为参数。 我们要清除整个画布,所以我们设置这些值00innerWidthinnerHeight

canvas.jslet x = 200;
let y = 200;const animate = () => {requestAnimationFrame(animate);c.clearRect(0, 0, innerWidth, innerHeight)
c.beginPath();
c.arc(x, y, 30, 0, Math.PI * 2, false );
c.strokeStyle = "pink";
c.stroke(); x += 1
y += 1
}animate()

Now the circle moves! But off the screen. We’re going to fix that, but first let’s think about what x += 1 and y += 1 are doing. They’re incrementing the x and y position of our circle by one pixel on every repaint. We can think of this as our velocity, so as it’s currently set up, our velocity is 1. Let’s make this a bit more dynamic and set variables dx and dy outside of our function that will represent the velocity of x and y. Now inside of our function we can increment x by dx and y by dy instead. We need some conditionals now to keep our circles x and y coordinates from exceeding the height and width of our canvas element. We can do this by changing the sign of our velocity values right before we exceed the innerHeight or innerWidth values of our canvas:

现在,圆圈移动了! 但是在屏幕上。 我们将解决此问题,但首先让我们考虑一下x += 1y += 1在做什么。 他们在每次重绘时将圆的xy位置增加一个像素。 我们可以将其视为速度,因此,当前设置的速度为1。让它更具动态性,并在函数外部设置代表dx和y速度的变量dxdy 。 现在,在函数内部,我们可以将x增加dx ,将y增加dy 。 现在我们需要一些条件,以防止圆的xy坐标超过画布元素的高度和宽度。 我们可以通过在超过画布的innerHeightinnerWidth值之前更改速度值的符号来做到这一点:

canvas.jslet x = 200;
let dx = 5;
let y = 200;
let dy = 5;
let radius = 30const animate = () => {
requestAnimationFrame(animate);c.clearRect(0, 0, innerWidth, innerHeight);
c.beginPath();
c.arc(x, y, radius, 0, Math.PI * 2, false );
c.strokeStyle = "pink";
c.stroke();if(x + radius > innerWidth || x - radius < 0){
dx = -dx
}if(y + radius > innerHeight || y - radius < 0){
dy = -dy
} x += dx;
y += dy;
}animate()

Next we want to randomize the velocity of our circle and position so that it doesn’t have the same path each time. We can start by randomizing x and y, which will give the circle a different starting point whenever we reload the page. We want to also randomize velocities so they can be positive or negative to change up the direction in which the circle appears to be moving upon a refresh:

接下来,我们要随机化圆和位置的速度,以使其每次都不具有相同的路径。 我们可以从随机化xy ,这在每次重新加载页面时都会为圆提供一个不同的起点。 我们还希望将速度随机化,以便它们可以是正值或负值,以改变刷新时圆的运动方向:

canvas.jslet x = Math.random() * innerWidth;
let dx = (Math.random() - 1) * 10;
let y = Math.random() * innerHeight;
let dy = (Math.random() - 1) * 10;
let radius = 30;const animate = () => {
requestAnimationFrame(animate);c.clearRect(0, 0, innerWidth, innerHeight);
c.beginPath();
c.arc(x, y, radius, 0, Math.PI * 2, false );
c.strokeStyle = "pink";
c.stroke();if(x + radius > innerWidth || x - radius < 0){
dx = -dx
}if(y + radius > innerHeight || y - radius < 0){
dy = -dy
} x += dx;
y += dy;
}animate()

This is great, we have a circle and it moves. But wouldn’t it be so much better if we had a hundred circles that moved? To do this we’ll need to create a bunch of circle objects. The fastest and most efficient way to do this will be to create a circle class from which we can instantiate new instances of circle objects. This way they will all have access to the same methods through prototypal inheritance. Each will need its own x, y, dx, dy, and radius values, so we’ll pass those in to the constructor function as arguments. We can then move the code we initially used to draw our circle into a function we’ll create in our class called draw(). We’ll need a second function update() that will update the velocity, so we can create that within our class as well. We can again take the previously written code updating our circles velocity and place it in our new update function. We’ll have to call draw() inside update() to actually get the circle on the screen and then call update() in animate()

太好了,我们有一个圆圈并且它在移动。 但是,如果我们有一百个圆圈运动,会不会更好呢? 为此,我们需要创建一堆圆形对象。 最快,最有效的方法是创建一个圈子类,从中我们可以实例化圈子对象的新实例。 这样,他们都可以通过原型继承访问相同的方法。 每个都需要自己的xydxdyradius值,因此我们会将它们作为参数传递给构造函数。 然后,我们可以将最初用于绘制圆的代码移到将在类draw()创建的函数中。 我们需要第二个函数update()来更新速度,因此我们也可以在我们的类中创建它。 我们可以再次使用先前编写的代码来更新圆速度,并将其放置在新的更新函数中。 我们必须在update() draw()内部调用draw()才能在屏幕上实际获得圆圈,然后在animate()调用update() animate()

canvas.jsclass Circle {
constructor(x, y, dx, dy, radius){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius
}draw() {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
c.strokeStyle = "pink";
c.stroke();
}update() {
if(this.x + this.radius > innerWidth || this.x - this.radius < 0){
this.dx = -this.dx
}
if(this.y + this.radius > innerHeight || this.y - this.radius < 0){
this.dy = -this.dy
}this.x += this.dx;
this.y += this.dy;
this.draw();
}
}let x = Math.random() * innerWidth;
let dx = (Math.random() - 0.5) * 10;
let y = Math.random() * innerHeight;
let dy = (Math.random() - 0.5) * 10;
let radius = Math.random() * 50;const circle = new Circle(x, y, dx, dy, radius)const animate = () => {
requestAnimationFrame(animate);c.clearRect(0, 0, innerWidth, innerHeight)
circle.update()
}animate()

So far we still only have one circle. We can solve that by using a simple for loop to create as many circle objects as we want, in this case 100, and push those onto an array. We’ll move our variable declarations for x, y, dx, dy, and radius in this loop as well:

到目前为止,我们仍然只有一个圈子。 我们可以通过使用简单的for循环来创建所需的任意数量的圆对象(在本例中为100)并将其推入数组来解决该问题。 我们dy在此循环中移动xydxdyradius的变量声明:

canvas.js....
const circleArray = []for(let i = 0; i < 100; i++){let radius = Math.random() * 50
let x = Math.random() * (innerWidth - radius * 2) + radius;
let dx = (Math.random() - 1.5) * 5
let y = Math.random() * (innerHeight - radius * 2) + radius;
let dy = (Math.random() - 1.5) * 5circleArray.push(new Circle(x, y, dx, dy, radius))
}
....

Now we have all of our circle instances, but how do we get them to show? Well we have them all in an array, so let’s create another for loop and put it inside of animate() where we call update on all of the circles we just created. If you recall, we have draw(), and update(). draw() creates a new circle with the values we pass in. update() then adjusts the x and y position of the circle using the velocity values we passed in. It then calls draw() to actually get the circle on the page. By putting the for loop that iterates through all of our circles in our animate page, we are both drawing and updating the position of every circle we created every time the page repaints:

现在我们有了所有的圈子实例,但是如何显示它们呢? 好吧,我们将它们全部放置在一个数组中,因此让我们创建另一个for循环,并将其放入animate()内,在此我们对刚创建的所有圆调用update。 如果您还记得,我们有draw()update()draw()使用传入的值创建一个新的圆update()然后使用传入的速度值调整圆的x和y位置。然后调用draw()在页面上实际获得该圆。 通过将遍历所有圆的for循环放入动画页面中,我们将绘制并更新每次重新绘制页面时创建的每个圆的位置:

cavans.jsclass Circle {
constructor(x, y, dx, dy, radius){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.color = Math.floor(Math.random()*16777215).
toString(16);}draw() {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false );
c.fillStyle = `#${this.color}`;
c.fill();
c.strokeStyle = `#${this.color}`;
c.stroke();
}update() {
if(this.x + this.radius > innerWidth || this.x - this.radius < 0){
this.dx = -this.dx
}
if(this.y + this.radius > innerHeight || this.y - this.radius < 0){
this.dy = -this.dy
}this.x += this.dx;
this.y += this.dy;this.draw()
}}const circleArray = [];for(let i = 0; i < 100; i++){
let radius = Math.random() * 50
let x = Math.random() * (innerWidth - radius * 2) + radius;
let dx = (Math.random() - 1.5) * 5
let y = Math.random() * (innerHeight - radius * 2) + radius;
let dy = (Math.random() - 1.5) * 5circleArray.push(new Circle(x, y, dx, dy, radius))
}const animate = () => {
requestAnimationFrame(animate);
c.clearRect(0, 0, innerWidth, innerHeight)for(let i = 0; i < circleArray.length; i++){
circleArray[i].update()
}
}animate()

Finally all our circles all move in different directions and at different speeds! This was just the tip of the iceberg in terms of what you can achieve with the HTML canvas element and javascript.

最终,我们所有的圈子都朝着不同的方向,以不同的速度运动! 就您可以使用HTML canvas元素和javascript实现的功能而言,这只是冰山一角。

Image for post

翻译自: https://medium.com/@rachael.ghorbani/basic-animation-using-the-html-canvas-and-javascript-94689501c118

canvas基本动画

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值