canvas动画:黑客帝国_使用Canvas API进行动画处理-第3部分:重力和动态渲染

canvas动画:黑客帝国

Over in Part 2 of this series we created a ball that would ricochet around the screen and change color when it collided with a border. Now we’re going to use what we learned to make this rain animation that dynamically renders drops with particle effect as each drop hits the bottom of our canvas.

在本系列的第2部分中 ,我们创建了一个球,该球会在屏幕周围跳动并在与边框碰撞时改变颜色。 现在,我们将使用我们学到的知识制作这个雨动画 ,当每个液滴击中画布的底部时, 该动画将动态渲染具有粒子效果的液滴。

样板 (Boilerplate)

Since we’re going to be working so close to the bottom of the screen we should hide any horizontal scroll bars with overflow: hidden, and we’ll darken it a bit so be a bit less eye burning.

由于我们要在屏幕底部附近工作,因此我们应该隐藏所有带有overflow: hidden水平滚动条overflow: hidden ,然后将其变暗一些,以免造成眼睛灼伤。

index.html
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
    <title>HTML Canvas</title>
    <style>
      body {
        overflow: hidden;
        background-color: #1a202c;
      }
    </style>
  </head>
  <body>

    <canvas></canvas>

  </body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
  <script src="./canvas.js"></script>
</html>
canvas.js
canvas.js
// Utilities
const randomNum = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
const randomColor = colors => colors[Math.floor(Math.random() * colors.length)];

// Get canvas element
const canvas = document.querySelector('canvas');
const c = canvas.getContext('2d');

// Make canvas fullscreen
canvas.width = innerWidth;
canvas.height = innerHeight;
addEventListener('resize', () => {
  canvas.width = innerWidth;
  canvas.height = innerHeight;
});

// Control Panel
const gui = new dat.GUI();

const controls = {
  count: 0,
  velocity: 0,
};

gui.add(controls, 'dx', 0, 10);
gui.add(controls, 'dy', 0, 10);

// New Object
class Ball {
  constructor(x, y, radius, color) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.color = color;
  }
};

Ball.prototype.draw = function () {
  c.beginPath();
  c.fillStyle = this.color;
  c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
  c.fill();
  c.closePath();
};

Ball.prototype.update = function () {
  this.x += controls.dx;
  this.y += -controls.dy;
  this.draw();
};

const ball = new Ball(innerWidth / 2, innerHeight / 2, 50, 'red');

// Handle changes
const animate = () => {
  requestAnimationFrame(animate);

  c.clearRect(0, 0, canvas.width, canvas.height);

  ball.update();
};

animate();

下降 (Drop)

Let’s start by getting our main drops working. We just need to store all of the variables for each drop as an object, draw a line on the screen, and add some value to the y position whenever update is ran to make it move downward.

让我们开始让我们的主要drops开始工作。 我们只需要将每个放置的所有变量存储为一个对象,在屏幕上画一条线,并在每次运行update使其向下移动时在y位置添加一些值即可。

canvas.js
canvas.js
class Drop {
  constructor(x, y, dy, thickness, length, color) {
    this.x = x;
    this.y = y;
    this.dy = dy;
    this.thickness = thickness;
    this.length = length;
    this.color = color;
  }
};

Drop.prototype.draw = function () {
  c.beginPath();
  c.strokeStyle = this.color;
  c.lineWidth = this.thickness;
  c.moveTo(this.x, this.y);
  c.lineTo(this.x, this.y - this.length);
  c.stroke();
  c.closePath();
}

Drop.prototype.update = function () {
  this.y += this.dy;

  this.draw();
}

Let’s render one onto the center of the canvas screen to see if it’s working.

让我们在画布屏幕的中央渲染一个,看看它是否在工作。

const drop = new Drop(innerWidth / 2, innerHeight / 2, 2, 5, 30, 'red');

const animate = () => {
  requestAnimationFrame(animate);
  c.clearRect(0, 0, canvas.width, canvas.height);

  drop.update();
};

animate();

股票代号 (Ticker)

That’s nice, but we’re going to need a lot more rendering along the top. To have multiple drops we could just make an array, use a for loop to assign random values to each item before pushing them to the array, and use forEach to update each of them per frame. But just using a for loop would only render the drops once, which would all move past our canvas out of site. So we have to be a bit creative to continually add new drops while removing all drops that move below our screen.

很好,但是沿着顶部我们将需要更多渲染。 要获得多个drop,我们可以制作一个数组,使用for循环为每个项目分配随机值,然后再将它们推入数组,并使用forEach每帧更新每个项目。 但是,仅使用for循环只会渲染一次拖放,所有这些都会从画布移出站点。 因此,我们必须具有一定的创造力,以便在删除屏幕下方移动的所有液滴的同时不断添加新的液滴。

To do this, we’re going to make a ticker that will count up by one every frame, every time it’s perfectly divisible by some number we’ll add a new drop to the array. Whatever number we divide it by will control how often new drops are rendered. To remove them, and save on processing power, we’ll just splice them out of the array when they’re past the bottom.

为此,我们将制作一个行情自动收录器,每帧将其完全除以某个数字,便会在该帧中加一新滴。 无论我们用多少除以它,都将控制渲染新液滴的频率。 为了删除它们并节省处理能力,我们将在它们超过底部时将它们拼接出阵列。

Using the modulo operator (%) we can divide a number and check if the remainder equals 0. So the higher it is the less often new drops will be rendered.

使用取模运算符( % ),我们可以除以一个数,然后检查余数是否等于0。因此,它越高,呈现新液滴的频率就越低。

While we’re here let’s give them some color. I find that using different values of the same color, along with random thickness and length, helps to give the illusion of some depth. I recommend checking out Kuler for your color palettes.

当我们在这里时,让我们给他们一些颜色。 我发现使用相同颜色的不同值以及随机的厚度和长度有助于给出某种深度的错觉。 我建议您查看Kuler的调色板。

canvas.js
canvas.js
const colors = [ '#9C4AFF', '#8C43E6', '#7638C2', '#5E2C99', '#492378'];

let drops = [];
let ticker = 0;
const animate = () => {
  requestAnimationFrame(animate);
  // Try using the 'residue' effect from Part 2
  // c.fillStyle = 'rgba(33, 33, 33, .3)'; //Lower opacity creates a longer tail
  // c.fillRect(0, 0, canvas.width, canvas.height);
  c.clearRect(0, 0, canvas.width, canvas.height);

  drops.forEach((drop, index) => {
    drop.update();
    if (drop.y >= canvas.height) drops.splice(index, 1);
  });

  // Timing between drops
  ticker++;
  let count = controls.count === 0 ? 0 : randomNum(controls.count + 5, controls.count);
  if (ticker % count == 0) {
    const x = randomNum(0, innerWidth);
    const y = 0;
    const dy = controls.velocity === 0 ? 0 : randomNum(controls.velocity, controls.velocity + 10);
    const thickness = randomNum(3, 5);
    const length = randomNum(20, 50);

    drops.push(new Drop(x, y, dy, thickness, length, randomColor(colors)));
  };
};

水滴和重力 (Droplets and Gravity)

To create our splash effect as they hit the ground we’re going to need some smaller particles that will have a gravity-like effect, arcing out from the main drop. Our Droplet class is going to be pretty similar to our main Drop, with a few differences since the droplets will be circles instead of lines.

为了在飞溅到地面时产生飞溅效果,我们需要一些较小的粒子,这些粒子具有类似重力的作用,并从主液滴中飞出。 我们的Droplet类将与我们的主Drop类非常相似,但有所不同,因为液滴将是圆形而不是直线。

canvas.js
canvas.js
class Droplet {
  constructor(x, y, dx, dy, radius, color) {
    this.x = x;
    this.y = y;
    this.dx = dx;
    this.dy = dy;
    this.radius = radius;
    this.color = color;
    this.gravity = .1;
  }
};

Droplet.prototype.draw = function () {
  c.beginPath();
  c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
  c.fillStyle = this.color;
  c.fill();
  c.closePath();
};

The main thing we want to worry about is our gravity. Gravity causes our downward movement to increase, so we’ll want to add this to our dy on the update method. So when we generate many droplets moving up, with a negative dy value, and add our gravity value onto it every frame it will slow down, reverse direction, and speed up until it is removed past our canvas. I made a simpler example with just the droplets, you can experiment with here.

我们要担心的主要事情是重力。 重力作用使我们向下运动增加,因此我们需要把它添加到我们的dyupdate方法。 因此,当我们生成许多带有负dy值的液滴向上移动并在其上添加重力值时,它会减速,反向并加快速度,直到将其移出画布为止。 我仅用液滴做了一个简单的示例,您可以在此处进行试验。

Droplet.prototype.update = function () {
  this.dy += this.gravity;
  this.y += this.dy;
  this.x += this.dx;

  this.draw();
};

And we’ll update and remove them just like our main drops.

我们将像我们的主要drop一样更新和删除它们。

let droplets = [];
const animate = () => {
  droplets.forEach((droplet, index) => {
    droplet.update();
    if (droplet.y >= canvas.height) droplets.splice(index, 1);
  });
};

(Splash)

Adding our particles is actually very simple, we can just use a for loop to generate them with the main drops position and pass-in some random values for the rest. Let’s also add some gravity to our drops to make them fall a bit more realistically.

添加我们的粒子实际上非常简单,我们可以使用for循环使用主液滴位置生成它们,并为其余粒子传入一些随机值。 让我们在水滴上增加一些重力,使其更真实地下落。

canvas.js
canvas.js
class Drop {
  constructor(x, y, dy, thickness, length, color) {
    this.x = x;
    this.y = y;
    this.dy = dy;
    this.thickness = thickness;
    this.color = color;
    this.length = length;
    this.gravity = .4;
  }
};

Drop.prototype.update = function () {
  // Stops drops if velocity controller is set to 0
  if (this.dy > 0) this.dy += this.gravity;
  this.y += this.dy;

  // It runs splash over the whole length of the drop, to we'll narrow it down to the end.
  if (this.y > canvas.height - 100) this.splash(this.x, this.y + (this.length * 2));

  this.draw();
}

Drop.prototype.splash = function (x, y) {
  for (let i = 0; i < 5; i++) {
    const dx = randomNum(-3, 3);
    const dy = randomNum(-1, -5);
    const radius = randomNum(1, 3);

    droplets.push(new Droplet(x, y, dx, dy, radius, randomColor(colors)));
  };
};

结论 (Conclusion)

While there’s still an enormous amount he learn about HTML canvas, hopefully this short series was a gentle enough introduction to its possibilities. Most sites are similar in a lot of ways but the ability to create custom animations offers a uniqueness that the most popular automated site builder tools never will.

尽管他对HTML画布了解很多,但希望这个简短的系列对它的可能性有足够的介绍。 大多数站点在很多方面都是相似的,但是创建自定义动画的能力提供了一种独特性,而大多数流行的自动化站点构建器工具却从未如此。

翻译自: https://www.digitalocean.com/community/tutorials/js-canvas-animations-gravity

canvas动画:黑客帝国

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值