媒介
上篇主假如理论的概述,本篇会多些实践,来讲讲canvas的基本用法,并包括一些基本三角函数的运用,引荐没有canvas基本的朋侪浏览,熟习的朋侪能够跳过。
本人才有限,迎接牛人配合议论,批评指正。
一起来画画吧
canvas的API有许多,假如逐一列举30分钟你是相对看不完的,而且怎样流水账还不如本身去看文档呢(笑),本教程的思绪是用实例一步一步从无到有解说基本用法。
canvas相干文档
准备事情
- 安排画布:经由历程增加
<canvas>
标签,增加canvas元素; - 猎取画布:经由历程
<canvas>
标签的id,取得canvas对象; - 取得画笔:经由历程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,以下图:
而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);
}());
};
其他示例
其他的运用就不逐一解说,排列出来一些: