h5canvas动画_创建一个接近图动画,介绍html5 canvas和动画循环

h5canvas动画

I’m trying to make the best out of these quarantine days, and learning new things has been my favorite pastime. A few days ago, I stumbled upon How to GraphQL while working through the Gatsby tutorial. I really liked the background animation, and I decided to replicate that using the HTML5 Canvas.

在这些隔离日里,我试图做到最好,而学习新事物一直是我最喜欢的消遣方式。 几天前,在研究Gatsby教程时,我偶然发现了如何使用GraphQL 。 我非常喜欢背景动画,因此决定使用HTML5 Canvas复制该动画。

Image for post
How to GraphQL background animation 如何GraphQL背景动画

If you are curious to see the end result, I have published a demo and the source code on GitHub and CodePen.

如果您想看到最终结果,我已经在GitHubCodePen上发布了一个演示和源代码。

Without further ado, let’s get started.

事不宜迟,让我们开始吧。

让我们定义工作 (Let’s Define the Work)

The animation works by moving points in a fixed direction with constant speed and by drawing lines between any two points that are close to each other.

动画通过以恒定速度沿固定方向移动点并在彼此靠近的任意两个点之间绘制线来进行动画处理。

Based on that, we’ll break down our work into the following tasks:

基于此,我们将把我们的工作分解为以下任务:

  • Set up the HTML and Canvas

    设置HTML和Canvas
  • Draw the points

    画点
  • Implement the animation loop

    实现动画循环
  • Move the points

    移动点
  • Draw the lines

    画线
  • Polish

    抛光

设置HTML和Canvas (Set Up the HTML and Canvas)

We’ll start by creating a basic HTML layout with a <canvas> element.

我们将从创建具有<canvas>元素的基本HTML布局开始。

We’ve given an ID to the <canvas> element, which we’ll use to access the element and its API via JavaScript, as well as width and height in pixels.

我们为<canvas>元素提供了一个ID,我们将使用它通过JavaScript来访问该元素及其API ,以及以像素为单位的widthheight

画点 (Draw the Points)

With the<canvas> element in place, we can start drawing things on it by using JavaScript and the 2D-rendering context API.

放置好<canvas>元素后,我们可以开始使用JavaScript和2D渲染上下文API在其上进行绘制。

The code above draws a black circle with a radius of 1 pixel in the middle of the canvas.

上面的代码在画布中间绘制一个半径为1像素的黑色圆圈。

If you are having trouble following along, you can use the CodePen below:

如果继续进行时遇到麻烦,可以使用下面的CodePen:

We will now generate a fixed amount of 20 points that will be randomly positioned on the canvas. For each point, we will create an object that initially will contain the point’s x and y positions. Once created, the objects will be added to a list we will later use to iterate on and draw each one of the points.

现在,我们将生成固定数量的20个点,这些点将随机放置在画布上。 对于每个点,我们将创建一个对象,该对象最初将包含该点的x和y位置。 创建对象后,这些对象将被添加到列表中,稍后我们将使用该列表进行迭代并绘制每个点。

We also created a helper function that relies on Math.random() to generate a random number in a given range. This function is used when a point is created, allowing us to position it randomly within the canvas’ borders.

我们还创建了一个依赖函数Math.random()来生成给定范围内的随机数的辅助函数。 创建点时使用此功能,使我们可以将其随机放置在画布的边界内。

It’s time to draw the points. We will make a few changes to the code we had for drawing a point and use it when iterating the list of points.

现在该是要点了。 我们将对绘制点的代码进行一些更改,并在迭代点列表时使用它。

By putting that all together, we have all our random points drawn on the canvas.

通过将所有这些放在一起,我们可以在画布上绘制所有随机点。

You can see it live with the CodePen below:

您可以在下面的CodePen中看到它:

实施动画循环 (Implement the Animation Loop)

We know that the points will have to move around. For that to happen, they will need to be animated. One definition of animation I like to quote is:

我们知道要点必须四处移动。 为此,需要对它们进行动画处理。 我想引用的动画定义之一是:

“An animation is nothing more than a visualization of change — a change that occurs over a period of time.” — Kirupa Chinnathambi

“动画无非是变化的可视化-一段时间内发生的变化。” 基鲁帕·基纳塔比

Luckily, modern browsers provide a very handy method called requestAnimationFrame that we can use to animate the points on the canvas. The method takes a callback as an argument to be invoked by the browser when it’s about to repaint the screen.

幸运的是,现代浏览器提供了一种非常方便的方法,称为requestAnimationFrame ,我们可以使用它为画布上的点设置动画。 该方法将回调作为要在重新绘制屏幕时由浏览器调用的参数。

A callback is a function we provide that we’ll use to process the points’ positions and to determine whether or not two points should be connected. The callbacks are usually invoked 60 times per second, or once every 1/60th of a second (which is roughly 16ms).

回调是我们提供的函数,可用于处理这些点的位置并确定是否应该连接两个点。 回调通常每秒调用60次,或者每1/60秒调用一次(大约16毫秒)。

Once we’re done processing, the browser will then render a new frame with the points and their connections on their new positions. With enough frames, our animation will come to life!

完成处理后,浏览器将渲染一个新框架,其中的点及其连接位于新位置。 有了足够的帧,我们的动画就会栩栩如生!

We’ll now create the animation loop.

现在,我们将创建动画循环。

That’s it. Thanks to requestAnimationFrame, all we need is four lines and our loop function will be called whenever the browser is ready to render another frame, which, again, at 60 frames per second, is every ~16ms.

而已。 多亏了requestAnimationFrame ,我们只需要四行就可以了,只要浏览器准备渲染另一帧,我们的loop函数就会被调用,该帧又以每秒60帧的速度每16ms一次。

There are more things to consider when implementing an animation loop with JavaScript. I recommend reading about dealing with inactive tabs and about the timing problem, since I won’t be talking about those in this article.

使用JavaScript实现动画循环时,还需要考虑更多事项。 我建议阅读有关处理非活动选项卡计时问题的信息 ,因为在本文中我不会谈论这些内容。

移动点 (Move the Points)

When we defined the animation we are creating, we said that points were to move in a fixed direction with constant speed. We can use a 2D vector to represent both the direction and the speed of a point.

定义要创建的动画时,我们说的是要以恒定的速度沿固定的方向移动。 我们可以使用2D向量来表示点的方向和速度。

A vector has a horizontal (x) and a vertical (y) component, and a magnitude. The two components determine the direction/where the point is looking towards. It’s usually described by an angle (degrees or radians). The magnitude is the length of the vector, or in our case the speed of the point, which determines how fast the point is moving in the direction it is “facing” at a specific point in time.

向量具有水平(x)和垂直(y)分量以及大小。 这两个组件确定了该点所朝向的方向/位置。 通常用角度(度或弧度)来描述。 大小是矢量的长度,或者在我们的情况下是点的速度,它确定点在特定时间点朝其“面向”方向移动的速度。

We know we can use degrees and radians when working with direction, but what do we use for speed? As an example, we can say that a car is moving at 60 miles per hour, and a person is walking at 1.4 meters per second. There’s a simple formula we all learned in school that defines speed as equal to distance divided by time. What are the distance and time we are working with?

我们知道在进行方向操作时可以使用度数和弧度,但是我们如何使用速度呢? 例如,我们可以说一辆汽车以每小时60英里的速度行驶,一个人以每秒1.4米的速度行走。 我们在学校都学过一个简单的公式,它定义速度等于距离除以时间。 我们合作的距离和时间是多少?

The screen that you are looking at right now is made of pixels, and we can use the number of pixels to determine its logical display size. That’s the unit we also used when defining the width and height of the <canvas>: we set both to be 400px. As points will be moving on the canvas as well as on the screen, we can use pixels as the unit for our distance.

您现在正在查看的屏幕是由像素组成的,我们可以使用像素数来确定其逻辑显示大小。 这是我们在定义<canvas>的宽度和高度时还使用的单位:我们都将其设置为400px。 由于点将在画布上以及在屏幕上移动,因此可以使用像素作为距离的单位。

And, as we mentioned above, the browser will let us process our animation every ~16ms. We will refer to that as a frame duration, and that will be our time unit. That leaves us with speed being pixels per frame duration.

而且,如上所述,浏览器将让我们每隔16ms处理一次动画。 我们将其称为帧持续时间,这就是我们的时间单位。 剩下的速度就是每帧持续时间的像素数。

The actual values can be any number we’d like. We can experiment with different values to determine what works best.

实际值可以是我们想要的任何数字。 我们可以尝试不同的值来确定最有效的方法。

Now, back to moving the points. With the help of basic trigonometry, we can calculate the point’s displacement given its direction and speed with two equations:

现在,回到移动要点。 在基本三角学的帮助下,我们可以使用两个方程式,根据点的方向和速度来计算点的位移:

x = speed * cos(direction)y = speed * sin(direction)

x =速度* cos(方向)y =速度* sin(方向)

If we add the displacement to the current position, we will have the point’s next position.

如果将位移添加到当前位置,则将获得该点的下一个位置。

Enough math, let’s code.

数学足够了,让我们编写代码。

We created a function called movePoint that given a point object, now with two new properties s and d, updates the point’s position. This function will be called as part of the animation loop.

我们创建了一个名为movePoint的函数,该函数提供了一个点对象,现在具有两个新属性sd ,以更新该点的位置。 此函数将作为动画循环的一部分被调用。

We also need to change the point creation to set the two new properties.

我们还需要更改点创建以设置两个新属性。

We set d to be a random number between 0 and 360 degrees, so points will move in random directions, and s to be 1 px/fd.

我们将d设置为0到360度之间的随机数,因此点将沿随机方向移动,而s则为1 px / fd。

Putting together all we have so far, we should have moving points!

总结我们到目前为止拥有的所有内容,我们应该有个动态点!

See it live below:

观看下面的直播:

Wait a minute — why are the points leaving a trail? Well, that’s how the canvas work. It’s a limitation that, once a shape gets drawn, it stays that way. Fortunately, there’s an easy way to solve that. All we need to do is call clearRect(0, 0, canvas.width, canvas.height) at the start of the animation loop, and it will clear the canvas removing any shapes that have been drawn previously.

等一下-为什么这些点不留痕迹? 好吧,这就是画布的工作方式。 这是一个局限性,一旦绘制了形状,它就会保持这种状态。 幸运的是,有一种简单的方法可以解决该问题。 我们需要做的就是在动画循环开始时调用clearRect (0, 0, canvas.width, canvas.height) ,它将清除画布,删除之前绘制的任何形状。

画线 (Draw the Lines)

We will now draw the lines that connect any two points that are close enough. We will say two points are close enough when the distance between then is less than 100px.

现在,我们将绘制连接足够接近的任意两个点的线。 当两点之间的距离小于100px时,我们将说两个点足够接近。

How do we find all the pairs of points where the distance between them is 100px or less? For the sake of this article, we will chose a non-optimal but simple approach.

我们如何找到所有两对点之间的距离等于或小于100px? 为了本文的目的,我们将选择一种非最佳但简单的方法。

For each point, we will look at all the other points, calculate the distance, and decide whether or not a line needs to be drawn.

对于每个点,我们将查看所有其他点,计算距离,并确定是否需要绘制线。

There are several performance improvements we can make to this piece of code, for example:

我们可以对这段代码进行一些性能改进,例如:

  • Eliminate obvious points by checking if other.x > point.x + CONNECT_DISTANCE or other.x < point.x — CONNECT_DISTANCE or other.y > point.y + CONNECT_DISTANCE or other.y < point.y — CONNECT_DISTANCE

    通过检查other.x > point.x + CONNECT_DISTANCEother.x < point.x — CONNECT_DISTANCEother.y > point.y + CONNECT_DISTANCEother.y < point.y — CONNECT_DISTANCE消除明显的点

  • Keep track of the connections so lines are not drawn twice, point — other and other — point

    跟踪连接,这样就不会画线了两次, point — otherother — point

Regardless of the improvements, as long as we have two nested loops, the time complexity will still be O(n²). If you are interested in learning about better, though more complex, solutions to this problem, I recommend reading about Quadtrees, k-d trees, and range searching.

不管有什么改进,只要我们有两个嵌套循环,时间复杂度就仍然是O(n²) 如果您有兴趣学习更好,更复杂的解决方案,建议阅读Q uadtreek- d树范围搜索

Let’s see where we are at:

让我们看看我们在哪里:

Nice! We’re almost there.

真好! 我们快到了。

抛光 (Polish)

We’ve come a long way. We started with an empty canvas, and now we have points moving around and lines been drawn when they are close enough.

我们已经走了很长一段路。 我们从一块空的画布开始,现在我们有一些点可以移动,并且当它们足够接近时可以绘制线条。

There are still two more things that we want to do in this article: restore points once they move outside of the canvas and add the line-stretch effect.

在本文中,我们还有两件事要做:一旦将点移出画布,就将其还原,并添加线条拉伸效果。

There are several ways to approach the former. We could remove points from the list as soon as they move outside of the canvas. then for each point we remove, we add a new one back in a random position. We could also bounce them back as soon as they hit a border, or we could “teleport” to the opposite site à la PacMan.

有几种方法可以解决前者。 只要将点移出画布,我们便可以将其从列表中删除。 然后,对于我们删除的每个点,我们都会在随机位置重新添加一个新点。 我们也可以在他们撞到边界后立即将它们弹回去,或者我们可以“传送”到对面的la PacMan。

For this article, we will implement the first approach as we already have most of the code for it anyway.

对于本文,我们将实现第一种方法,因为无论如何我们已经拥有了大多数代码。

We will remove points that are out of bounds right after we process their next position in the animation loop.

在处理动画循环中的下一个位置之后,我们将立即删除超出范围的点。

As well as moving the point creation loop into the animation loop with a couple of minor changes, we will have to change points from const to let. We will also initialize i with points.length instead of 0. That way, we will always add new points as soon as some are removed.

除了通过一些小的更改将点创建循环移动到动画循环之外,我们还必须将pointsconst更改为let 。 我们还将使用points.length而不是0初始化i 。 这样,一旦删除某些点,我们将始终添加新点。

Regarding the line-stretch effect, if you pay close attention to the How To GraphQL background animation, the lines actually stretch thin as points move away from each other. We will try to replicate that by changing the opacity of the line based on the distance.

关于线条拉伸效果,如果您密切关注How to GraphQL背景动画,则线条实际上会随着点彼此远离而变细。 我们将尝试通过基于距离更改线的不透明度来重复该操作。

The complete JavaScript code:

完整JavaScript代码:

And here is a live demo:

这是一个现场演示:

Thanks for reading, I hope you learned something new today.

感谢您的阅读,希望您今天学到了一些新知识。

Take care, and I’ll see you next time!

保重,下次再见!

翻译自: https://medium.com/better-programming/creating-a-proximity-graph-animation-an-introduction-to-html5-canvas-and-the-animation-loop-45719d82d1a3

h5canvas动画

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值