canvas
<canvas> 标签定义图形,比如图表和其他图像,您必须使用脚本来绘制图形。
在画布上(Canvas)画一个红色矩形,渐变矩形,彩色矩形,和一些彩色的文字。
什么是 Canvas?
HTML5 的 canvas 元素使用 JavaScript 在网页上绘制图像。
画布是一个矩形区域,您可以控制其每一像素。
canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。
创建一个画布(Canvas)
向 HTML5 页面添加 canvas 元素。
默认情况下 <canvas> 元素没有边框和内容。
<canvas id="myCanvas" width="200" height="100"></canvas>
通过 JavaScript 来绘制
canvas 元素本身是没有绘图能力的。所有的绘制工作必须在 JavaScript 内部完成:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 150, 75);
JavaScript 使用 id 来寻找 canvas 元素:
var c=document.getElementById("myCanvas");
然后,创建 context 对象:
var ctx = c.getContext("2d");
getContext("2d") 对象是内建的 HTML5 对象,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。
下面的两行代码绘制一个红色的矩形:
ctx.fillStyle="#FF0000";
ctx.fillRect(0,0,150,75);
fillStyle 方法将其染成红色,fillRect 方法规定了形状、位置和尺寸。
理解坐标
上面的 fillRect 方法拥有参数 (0,0,150,75)。
意思是:在画布上绘制 150x75 的矩形,从左上角开始 (0,0)。
如下图所示,画布的 X 和 Y 坐标用于在画布上对绘画进行定位。
更多 Canvas 实例
下面的在 canvas 元素上进行绘画的更多实例:
实例 - 线条
通过指定从何处开始,在何处结束,来绘制一条线: 定义开始坐标(0,0), 和结束坐标 (200,100)。然后使用 stroke() 方法来绘制线条:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.moveTo(0,0);
ctx.lineTo(200,100);
ctx.stroke();
实例 - 圆形
通过规定尺寸、颜色和位置,来绘制一个圆:
let canvas = document.querySelector("canvas")
let ctx = canvas.getContext("2d")
ctx.beginPath();
ctx.arc(95, 50, 40, 0, 2 * Math.PI);
ctx.stroke();
Canvas - 文本
使用 canvas 绘制文本,重要的属性和方法如下:
- font - 定义字体
- fillText(text,x,y) - 在 canvas 上绘制实心的文本
- strokeText(text,x,y) - 在 canvas 上绘制空心的文本
使用 fillText():
let canvas = document.querySelector("canvas")
let ctx = canvas.getContext("2d")
ctx.moveTo(0,100)
ctx.lineTo(600,100)
ctx.stroke()
ctx.font = "30px 楷体"
ctx.textBaseline = "center"
ctx.textBaseline = "center"
ctx.fillText('皮卡丘',100,100)
使用 strokeText():
使用 "Arial" 字体在画布上绘制一个高 30px 的文字(空心):
var c=document.getElementById("canvas");
var ctx=c.getContext("2d");
ctx.font="30px Arial";
ctx.strokeText("Hello World",10,50);
阴影
shadowOffsetX、shadowOffsetY
shadowOffsetX 和 shadowOffsetY 用来设定阴影在 X 和 Y 轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为 0。
shadowBlur
shadowBlur 用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0。
shadowColor
shadowColor 是标准的 CSS 颜色值,用于设定阴影颜色效果,默认是全透明的黑色。
举个例子看一下:
// 获取 canvas 元素
var canvas = document.querySelector('canvas');
// 通过判断getContext方法是否存在来判断浏览器的支持性
if(canvas.getContext) {
// 获取绘图上下文
var ctx = canvas.getContext('2d');
ctx.font = "50px serif"; // 设置文案大小和字体
ctx.shadowColor = "#cccccc"; // 设置阴影颜色
ctx.fillStyle = "#ee7934"; // 设置填充颜色
ctx.shadowOffsetX = 10; // X轴上的阴影
ctx.shadowOffsetY = 10; // Y轴上的阴影
ctx.shadowBlur = 5; // 阴影的模糊程度
ctx.fillText("Hi Canvas !", 100, 50);
ctx.fillRect(100, 100, 200, 100);
ctx.shadowOffsetX = -10;
ctx.shadowOffsetY = -10;
ctx.shadowBlur = 5;
ctx.fillText("Hi Canvas !", 100, 300);
ctx.fillRect(100, 350, 200, 100);
}
透明度
除了绘制实色的图形,还可以绘制有透明度的图形。通过设置 globalAlpha 属性或者使用有透明度的样式作为轮廓或填充都可以实现
举个例子看一下:
// 获取 canvas 元素
var canvas = document.querySelector('canvas');
// 通过判断getContext方法是否存在来判断浏览器的支持性
if(canvas.getContext) {
// 获取绘图上下文
var ctx = canvas.getContext('2d');
// 绘制一个矩形
ctx.beginPath();
// 指定透明度的填充样式
ctx.fillStyle = "rgba(0, 255, 0, 0.2)";
ctx.fillRect(10,10,300,100);
// 绘制一个矩形边框
ctx.beginPath();
// 指定透明度的描边样式
ctx.strokeStyle = "rgba(255, 0, 0, 0.7)";
ctx.strokeRect(10, 90, 100, 300);
// 绘制一个圆
ctx.beginPath()
ctx.fillStyle = "rgba(255, 255, 0, 1)";
// 设置透明度值
ctx.globalAlpha = 0.5;
ctx.arc(200, 200, 100, 0, Math.PI*2, true);
ctx.fill();
}
渐变
渐变分为两种,分别是线性渐变和径向渐变,在绘图中我们可以用线性或者径向的渐变来填充或描边。
线性渐变
语法: createLinearGradient(x1, y1, x2, y2),参数分别为 起点的坐标和终点的坐标。
在渐变的设置中还需要一个方法来添加渐变的颜色,语法为:gradient.addColorStop(offset, color),其中color就是颜色,offset 则是颜色的偏移值,只为0到1之间的数。
举个例子看一下:
// 获取 canvas 元素
var canvas = document.querySelector('canvas');
// 通过判断getContext方法是否存在来判断浏览器的支持性
if(canvas.getContext) {
// 获取绘图上下文
var ctx = canvas.getContext('2d');
// 创建渐变
var gradient1 = ctx.createLinearGradient(10, 10, 400, 10);
gradient1.addColorStop(0, "#00ff00");
gradient1.addColorStop(1, "#ff0000");
var gradient2 = ctx.createLinearGradient(10, 10, 400, 10);
// 从0.5的位置才开始渐变
gradient2.addColorStop(0.5, "#00ff00");
gradient2.addColorStop(1, "#ff0000");
ctx.beginPath()
ctx.fillStyle = gradient1;
ctx.fillRect(10, 10, 400, 100);
ctx.beginPath();
ctx.fillStyle = gradient2;
ctx.fillRect(10, 150, 400, 100);
}
径向渐变
语法:ctx.createRadialGradient(x0, y0, r0, x1, y1, r1),参数分别为开始圆的坐标和半径以及结束圆的坐标和半径。
举个例子看一下:
// 获取 canvas 元素
var canvas = document.querySelector('canvas');
// 通过判断getContext方法是否存在来判断浏览器的支持性
if(canvas.getContext) {
// 获取绘图上下文
var ctx = canvas.getContext('2d');
// 创建渐变
// 结束坐标为点
var gradient1 = ctx.createRadialGradient(100, 100, 100, 100, 100, 0);
gradient1.addColorStop(0, "#ff770f");
gradient1.addColorStop(1, "#ffffff");
// 结束坐标为半径30的圆
var gradient2 = ctx.createRadialGradient(320, 100, 100, 320, 100, 30);
gradient2.addColorStop(0, "#ff770f");
gradient2.addColorStop(1, "#ffffff");
// 从0.5的位置才开始渲染
var gradient3 = ctx.createRadialGradient(100, 320, 100, 100, 320, 0);
gradient3.addColorStop(0.5, "#ff770f");
gradient3.addColorStop(1, "#ffffff");
// 开始坐标和结束坐标不一样
var gradient4 = ctx.createRadialGradient(320, 320, 100, 250, 250, 0);
gradient4.addColorStop(0, "#ff770f");
gradient4.addColorStop(1, "#ffffff");
ctx.beginPath();
ctx.fillStyle = gradient1;
ctx.fillRect(10, 10, 200, 200);
ctx.beginPath();
ctx.fillStyle = gradient2;
ctx.fillRect(220, 10, 200, 200);
ctx.beginPath();
ctx.fillStyle = gradient3;
ctx.fillRect(10, 220, 200, 200);
ctx.beginPath();
ctx.fillStyle = gradient4;
ctx.fillRect(220, 220, 200, 200);
}
动画
在 canvas 上绘制内容是用 canvas 提供的或者自定义的方法,而通常我们仅仅在脚本执行结束后才能看见结果,所以想在 for 循环里面完成动画是不可能的。那么为了实现动画,我们需要一些可以定时执行重绘的方法。
- setInterval(function, delay) :定时器,当设定好间隔时间后,function 会定期执行。
- setTimeout(function, delay):延时器,在设定好的时间之后执行函数
- requestAnimationFrame(callback):告诉浏览器你希望执行一个动画,并在重绘之前,请求浏览器执行一个特定的函数来更新动画。
如果不需要与用户互动,可以使用 setInterval() 方法,它可以定期执行指定的代码。如果需要做游戏,可以使用键盘或者鼠标事件配合上 setTimeout() 方法来实现。通过设置事件监听,可以捕捉用户的交互,并执行相应的动作。
下面我们采用 window.requestAnimationFrame()来实现一个动画效果。requestAnimationFrame()方法提供了更加平缓且有效率的方式来执行动画,当系统准备好重绘条件后才会调用绘制动画帧。一般每秒钟回调函数执行 60 次,也有可能会被降低,因为通常情况下requestAnimationFrame()方法会遵循 W3C 的建议,浏览器中的回调函数执行次数通常与浏览器屏幕刷新次数相匹配。还有为了提高性能和电池寿命,通常 requestAnimationFrame() 方法运行在后台标签页或者隐藏在 里时,requestAnimationFrame() 方法会暂停调用以提升性能和电池寿命。
举个例子看一下:
let sun;
let earth;
let moon;
let ctx;
function init(){
sun = new Image();
earth = new Image();
moon = new Image();
sun.src = "sun.png";
earth.src = "earth.png";
moon.src = "moon.png";
let canvas = document.querySelector("#solar");
ctx = canvas.getContext("2d");
sun.onload = function (){
draw()
}
}
init();
function draw(){
ctx.clearRect(0, 0, 300, 300); //清空所有的内容
/*绘制 太阳*/
ctx.drawImage(sun, 0, 0, 300, 300);
ctx.save();
ctx.translate(150, 150);
//绘制earth轨道
ctx.beginPath();
ctx.strokeStyle = "rgba(255,255,0,0.5)";
ctx.arc(0, 0, 100, 0, 2 * Math.PI)
ctx.stroke()
let time = new Date();
//绘制地球
ctx.rotate(2 * Math.PI / 60 * time.getSeconds() + 2 * Math.PI / 60000 * time.getMilliseconds())
ctx.translate(100, 0);
ctx.drawImage(earth, -12, -12)
//绘制月球轨道
ctx.beginPath();
ctx.strokeStyle = "rgba(255,255,255,.3)";
ctx.arc(0, 0, 40, 0, 2 * Math.PI);
ctx.stroke();
//绘制月球
ctx.rotate(2 * Math.PI / 6 * time.getSeconds() + 2 * Math.PI / 6000 * time.getMilliseconds());
ctx.translate(40, 0);
ctx.drawImage(moon, -3.5, -3.5);
ctx.restore();
requestAnimationFrame(draw);
}
总结一下绘制动画的基本步骤
- 清空 canvas:除非接下来要画的内容会完全充满 canvas(例如背景图),否则需要清空所有。最简单的做法就是用 clearRect 方法。
- 保存 canvas 状态:如果要改变 canvas 状态的设置(样式,变形之类的),之后又要在每画一帧之时都是原始状态的情况时,需要先保存一下。
- 绘制动画图形(animated shapes)
- 恢复 canvas 状态:如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。
高级动画
高级动画就是在初级动画的基础上加上一些符合物理的运动,这样就能使我们的动画更生动而不是那么的呆板。 下面我们一步步来实现一个小球的自由落体的运动。
绘制小球
首先我们先绘制一个小球,直接上代码:
// 获取 canvas 元素
var canvas = document.getElementById('canvas');
// 通过判断getContext方法是否存在来判断浏览器的支持性
if (canvas.getContext) {
// 获取绘图上下文
var ctx = canvas.getContext('2d');
var ball = {
x: 100,
y: 100,
radius: 25,
color: 'blue',
draw: function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
}
};
ball.draw();
}
速率
我们通过给小球添加速率矢量进行移动。这个依旧用requestAnimationFrame() 方法来实现,在每一帧里面,依旧用clear 清理掉之前帧里旧的圆形。
// 获取 canvas 元素
var canvas = document.getElementById('canvas');
// 通过判断getContext方法是否存在来判断浏览器的支持性
if (canvas.getContext) {
// 获取绘图上下文
var ctx = canvas.getContext('2d');
var ball = {
x: 100,
y: 100,
vx: 1,
vy: 3,
radius: 25,
color: 'blue',
draw: function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
}
};
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ball.draw();
// 添加速率
ball.x += ball.vx;
ball.y += ball.vy;
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
ball.draw();
}
边界
想让小球反弹那么我们就需要添加边界
// 获取 canvas 元素
var canvas = document.getElementById('canvas');
// 通过判断getContext方法是否存在来判断浏览器的支持性
if(canvas.getContext) {
// 获取绘图上下文
var ctx = canvas.getContext('2d');
var ball = {
x: 100,
y: 100,
vx: 1,
vy: 3,
radius: 25,
color: 'blue',
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
}
};
function draw() {
ctx.clearRect(0,0, canvas.width, canvas.height);
ball.draw();
// 添加速率
ball.x += ball.vx;
ball.y += ball.vy;
// 添加边界
if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
ball.vy = -ball.vy;
}
if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
ball.vx = -ball.vx;
}
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
ball.draw();
}
加速度
为了让动作更真实,我们还需要加入加速度的处理。
// 获取 canvas 元素
var canvas = document.getElementById('canvas');
// 通过判断getContext方法是否存在来判断浏览器的支持性
if(canvas.getContext) {
// 获取绘图上下文
var ctx = canvas.getContext('2d');
var ball = {
x: 100,
y: 100,
vx: 1,
vy: 3,
radius: 25,
color: 'blue',
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
}
};
function draw() {
ctx.clearRect(0,0, canvas.width, canvas.height);
ball.draw();
// 添加加速度
ball.vy *= .99;
ball.vy += .25;
// 添加速率
ball.x += ball.vx;
ball.y += ball.vy;
// 添加边界
if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
ball.vy = -ball.vy;
}
if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
ball.vx = -ball.vx;
}
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
ball.draw();
}
参考