【30分钟学完】canvas动画|游戏基本(2):从零开始画画

媒介

上篇主假如理论的概述,本篇会多些实践,来讲讲canvas的基本用法,并包括一些基本三角函数的运用,引荐没有canvas基本的朋侪浏览,熟习的朋侪能够跳过。
本人才有限,迎接牛人配合议论,批评指正。

一起来画画吧

canvas的API有许多,假如逐一列举30分钟你是相对看不完的,而且怎样流水账还不如本身去看文档呢(笑),本教程的思绪是用实例一步一步从无到有解说基本用法。
canvas相干文档

准备事情

  1. 安排画布:经由历程增加<canvas>标签,增加canvas元素;
  2. 猎取画布:经由历程<canvas>标签的id,取得canvas对象;
  3. 取得画笔:经由历程canvas对象的getContext(“2d”)要领,取得2D环境。
<canvas id="canvas" width="400" height="400"></canvas>
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');

画个箭头

起首我们来画个红边黄底的箭头,运用面向对象的代码构造体式格局,悉数代码以下。
类名为Arrow。它具有x轴坐标、y轴坐标、底的色彩color、扭转弧度rotation四个属性。
实例要领是draw(),它须要一个context对象作为参数,就是准备事情里的context,它就相称因而画笔,这里实际上是相似依靠注入的历程,将canvas的画笔交给实例的draw()要领,实例用这个画笔去画出箭头,绘画历程见代码解释。特别注重以下几点:

  • beginPath()要领挪用后moveTo()和lineTo搬动坐标是相对与beginPath()时画笔的坐标的,能够明白成画笔自带一个坐标系,它能够扭转和在画布上搬动,绘制事情的坐标都是属于这个坐标系的;
  • beginPath()是绘制设置状况的起始点,它以后代码设置的绘制状况的作用域完毕于绘制要领stroke()、fill()或许closePath();
  • save()的作用是保留笔的状况,由于一个画布的笔只要一支,会在差别对象中通报,为了不污染后续的画就应该先保留,画完再restore()复原;
  • <canvas>本身是通明的,能够运用CSS给它个背景,例子中广泛运用白色背景。
/**
 * 箭头类
 * @class Representing a arrow.
 */
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "Arrow" }] */
class Arrow {
  /**
    * Create a arrow.
    */
  constructor() {
    this.x = 0;
    this.y = 0;
    this.color = '#ffff00';
    this.rotation = 0;
  }
  /**
   * Draw the arrow.
   * @param {Object} _context - The canvas context.
   */
  draw(_context) {
    const context = _context;
    // 会先保留画笔状况
    context.save();
    // 搬动画笔
    context.translate(this.x, this.y);
    // 扭转画笔
    context.rotate(this.rotation);
    // 设置线条宽度
    context.lineWidth = 2;
    // 设置线条色彩
    context.strokeStyle = '#ff0000';
    // 设置添补色彩
    context.fillStyle = this.color;
    // 最先途径
    context.beginPath();
    // 将笔搬动到相对位置
    context.moveTo(-50, -25);
    // 画线到相对位置
    context.lineTo(0, -25);
    context.lineTo(0, -50);
    context.lineTo(50, 0);
    context.lineTo(0, 50);
    context.lineTo(0, 25);
    context.lineTo(-50, 25);
    context.lineTo(-50, -25);
    // 闭合途径
    context.closePath();
    // 添补途径围困区
    context.fill();
    // 绘制途径
    context.stroke();
    // 载入保留的笔信息
    context.restore();
  }
}

同理我们还能够画点其他的,比方一个圆ball.js,轻微多些参数,逐步明白。
制品的结果能够先看这个(轻微剧透):一个会跟踪鼠标位置的箭头

到场轮回动起来

如今我们已控制了画画的基本功,而且能够画箭头arrow.js和圆ball.js,但是如许只是静止画,接下来我们须要一个轮回,不停的实行擦除和重画的事情才完成帧动画。
下面这段代码的中绘图函数drawFrame被马上实行,并递归挪用本身,你将会在大部分例子中看到。
轮回原理上一篇已申明,不再重复。这里要申明的是clearRect(),这个函数接收一个矩形坐标,也就是(x轴坐标,y轴坐标,矩形宽度,矩形高度),用于消灭矩形区域内的画。
例子里直接是消灭了全部画布,但这不是相对的,刷不革新,是部分革新照样悉数革新,都须要灵活处理。
这里有个不革新的例子:鼠标绘图东西

(function drawFrame() {
  // 相似setTimeout的操纵
  window.requestAnimationFrame(drawFrame, canvas);
  // 将画布擦清洁
  context.clearRect(0, 0, canvas.width, canvas.height);
  // ...继承你的作画
}());

给它点动力

如今画面已是在不停的重绘,但为何照样静止的呢?由于每一次革新都没有转变要画的内容。
那我们就给它一个目标吧,如许它才动起来,比方就让箭头一直指向鼠标。
下面是中间代码,重要目标就是求出每帧arrow的扭转角度,这里运用的东西类mouse会及时返回鼠标的x,y轴坐标,封装原理上一篇已讲过,依据这鼠标的坐标和arrow的坐标,即可获得鼠标的相对于arrow的间隔dx和dy,以下图:

《【30分钟学完】canvas动画|游戏基本(2):从零开始画画》

而arrow的扭转角度即能够经由历程dx和dy运用横竖切函数获得,这里须要注重几点:

  • 仔细看上面代码中arrow的绘制历程,可知其原点是在中间位置的,所以恰好扭转角度就是画笔的扭转角度;
  • dx和dy是鼠标相对与arrow的坐标,所以图中把坐标系搬动箭头中间是没缺点的;
  • 用atan2,而不是atan,是由于tan值原本就多是重复的,比方-1/2和1/(-2)两个都是-0.5,没法辨别象限,而atan2就能够辨别开。

完全实例:一个会跟踪鼠标位置的箭头

window.onload = function () {
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const mouse = utils.captureMouse(canvas);
  const arrow = new Arrow();

  arrow.x = canvas.width / 2;
  arrow.y = canvas.height / 2;

  (function drawFrame() {
    window.requestAnimationFrame(drawFrame, canvas);
    context.clearRect(0, 0, canvas.width, canvas.height);
    const dx = mouse.x - arrow.x;
    const dy = mouse.y - arrow.y;

    arrow.rotation = Math.atan2(dy, dx);
    arrow.draw(context);
  }());
};

三角函数

高低活动

终究顺遂过渡到三角函数的话题(笑)。三角函数不止有横竖切一个运用,下面再看一个例子。
下面是一个ball在高低活动的中间代码,重点就是ball的y轴坐标转变,就是这句:

ball.y = clientY + Math.sin(angle) * range;

应用Math.sin(angle)的取值局限是-1到1,而且会跟着angle增大而重复,使ball在肯定局限高低活动。
完全例子:一个高低活动的球(可调参数版)

window.onload = function () {
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const ball = new Ball();
  let angle = 0;
  // 活动中间
  const clientY = 200;
  // 局限
  const range = 50;
  // 速率
  const speed = 0.05;

  ball.x = canvas.width / 2;
  ball.y = canvas.height / 2;

  (function drawFrame() {
    window.requestAnimationFrame(drawFrame, canvas);
    context.clearRect(0, 0, canvas.width, canvas.height);

    ball.y = clientY + Math.sin(angle) * range;
    angle += speed;
    ball.draw(context);
  }());
};

向前活动

只是高低活动不过瘾,那就让圆行进吧,实在就是每帧转变x轴的位置。
中间代码以下,比拟前面的高低活动,多了x轴的速率,每帧搬动一点就形成了波浪行进的结果。
完全实例:一个波浪活动的球

window.onload = function () {
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const ball = new Ball();
  let angle = 0;
  const centerY = 200;
  const range = 50;
  const xspeed = 1;
  const yspeed = 0.05;

  ball.x = 0;
  (function drawFrame() {
    window.requestAnimationFrame(drawFrame, canvas);
    context.clearRect(0, 0, canvas.width, canvas.height);
    ball.x += xspeed;
    ball.y = centerY + Math.sin(angle) * range;
    angle += yspeed;
    ball.draw(context);
  }());
};

其他示例

其他的运用就不逐一解说,排列出来一些:

    原文作者:calimanco
    原文地址: https://segmentfault.com/a/1190000013965650
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞