Canvas(基础)

标签:canvas 简单示例 入门知识
作者: 张耀国 ( IgorZhang )
E-mail: [email protected]


canvas 是 HTML5 新增的元素,可使用JavaScript脚本来绘制图形。例如:画图,合成照片,创建动画甚至实时视频处理与渲染。

基本用法


    您的浏览器不支持canvas,请升级您的浏览器!

canvas 标签只有两个属性—— width 和 height。这些都是可选的,当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。宽高只可以通过 JavaScript 进行修改。该元素可以使用CSS来定义大小,但在绘制时图像会伸缩以适应它的框架尺寸:如果CSS的尺寸与初始画布的比例不一致,它会出现扭曲。
如果浏览器版本过低,或者浏览器内核不支持canvas标签的话,那么标签内部的替换内容将会对用户做出提示,若浏览器支持canvas,替换内容将不会出现。

绘制前准备

在绘制之前我们必须先获取canvas元素以及开启2d绘图的渲染上下文(简称获取画笔)

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

代码的第一行通过使用 document.getElementById() 方法来为 canvas 元素得到DOM对象。一旦有了元素对象,你可以通过使用它的getContext() 方法来访问绘画上下文。

栅格

Canvas(基础)_第1张图片
栅格

在我们开始画图之前,我们需要了解一下画布栅格(canvas grid)以及坐标空间。如右图所示,canvas元素默认被网格所覆盖。通常来说网格中的一个单元相当于canvas元素中的一像素。栅格的起点为左上角(坐标为(0,0))。所有元素的位置都相对于原点定位。所以图中蓝色方形左上角的坐标为距离左边(Y轴)x像素,距离上边(X轴)y像素(坐标为(x,y))。

绘制路径

直线

绘制一条直线,我们需要两个点,即起点和终点。这两个点都将用坐标(x,y)表示。

// 获取元素
var canvas = document.getElementById("canvas");
// 获取上下文对象
// 获取“画笔”,大多数操作都是使用画笔进行
var ctx = canvas.getContext("2d");

// 画一条直线
// 1.开始绘制
ctx.beginPath();
// 2,放置起始点
ctx.moveTo(100, 100);
// 3.放置后续的点 (可以写任意多个)
ctx.lineTo(200, 200);
// 4.结束路径绘制(闭合路径) (非必需)
ctx.closePath();
// 5.画线
ctx.stroke();

此时,我们已经画出了一条直线

我们这里要注意了:
ctx.closePath(); 使用这个方法之后,会自动将起点与终点链接起来,如果不需要连接的话,可以不用写这个方法。但是如果要绘制第二个图形的话,第二个图形必须先使用ctx.beginPath(); 方法,防止与上个图形连接。

绘制一个三角形

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

// 1.开始绘制
ctx.beginPath();
// 2.放置起始点
ctx.moveTo(75,50);
// 3.放置后续两个点
ctx.lineTo(100,75);
ctx.lineTo(100,25);
// 4.闭合路径
ctx.closePath();
// 5.画线
ctx.stroke();

我们通过获取三个点之后用ctx.stroke()方法进行连接。就可以得到一个三角形。但是:我们此时绘制的仅仅是三条细长的直线连接。有没有什么方法可以修改它的样式呢? 有的,这里我们只需要设置几个属性即可。

// 设置线条宽度
ctx.lineWidth = 20;
// 设置线条颜色
ctx.strokeStyle = "red";
// 设置填充颜色
ctx.fillStyle = "brown";

// 设置线两端为圆角
// 在使用lineCap的时候,一定要非常小心closePath
ctx.lineCap = "round";
// 设置交叉线的交接为圆角
ctx.lineJoin = "round";

这里我们要介绍另一个方法了:fill(); (填充)

fill ( ) 方法能够将绘制的图形进行填充。在刚刚我们绘制三角形时,如果使用 fill ( ) 方法,我们将会获得一个经过颜色填充的三角形。那么我们可以 stroke()和 fill()两个方法同时使用吗? 没问题,这是可以的。

综合实例:绘制一个边框宽度为20像素,边框颜色为红色,填充颜色为黄色的三角形

ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(100,25);
ct.lineWidth = 20;
ctx.strokeStyle = "red";
ctx.stroke();
ctx.fillStyle = "yellow";
ctx.fill();

绘制矩形

绘制矩形,canvas 给我们提供了非常便捷的方法:
绘制一个矩形边框
strokeRect(x, y, width, height);
绘制一个填充后的矩形
fillRect(x, y, width, height);
擦除指定大小的矩形区域(可以理解为橡皮擦)
clearRect(x, y, width, height);

x , y 是相对于canvas左上角(原点)的坐标,同样。我们要绘制的矩形也将以 x, y为原点进行绘制。 width 和 height 即为我们要绘制矩形的宽和高。

  • 简单实例:
ctx.fillRect(25,25,100,100);
ctx.clearRect(45,45,60,60);
ctx.strokeRect(50,50,50,50);
//fillRect()函数绘制了一个边长为100px的黑色正方形。clearRect()函数从正方形的中心开始擦除了一个60*60px的正方形,接着strokeRect()在清除区域内生成一个50*50的正方形边框。

实现效果:

Canvas(基础)_第2张图片

综合实例:绘制一个长宽为200px,边框为10px红色,填充颜色为绿色的,且中间部分矩形区域透明的矩形

ctx.fillStyle = "green";
ctx.lineWidth = "10px";
ctx.strokeStyle = "red";
ctx.strokeRect( 100, 100, 200, 200);
ctx.strokeRect( 100, 100, 200, 200);
ctx.clearRect( 150, 150, 100, 100);

绘制圆和圆弧

绘制圆弧或者圆,我们使用arc()方法。

arc(x, y, radius, startAngle, endAngle, anticlockwise)
画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。

arcTo(x1, y1, x2, y2, radius)
根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。

该方法有五个参数:x,y为绘制圆弧所在圆上的圆心坐标。radius为半径。startAngle以及endAngle参数用弧度定义了开始以及结束的弧度。这些都是以x轴为基准。参数anticlockwise 为一个布尔值。为true时,是逆时针方向,否则顺时针方向。

注意:arc()函数中的角度单位是弧度,不是度数。角度与弧度的js表达式:radians=(Math.PI/180)*degrees。

小例子:绘制笑脸

// 笑脸
ctx.beginPath();
ctx.arc(250,430,70, 0,Math.PI*2, true);
ctx.stroke();
ctx.beginPath();
ctx.arc(250,430,50, 0,Math.PI, false);
ctx.stroke();
ctx.beginPath();
ctx.arc(230,410,10, 0,Math.PI*2, true);
ctx.stroke();
ctx.beginPath();
ctx.arc(270,410,10, 0,Math.PI*2, true);
ctx.stroke();

小例子:绘制一个绘制一个由多个随机颜色不同大小的圆组成的镖靶

// 定义一个函数,用于获取随机颜色
function random () {
    return parseInt(Math.random() * 255);
}
// 用for循环创建10个填充了随机颜色的圆形
for( var i = 10; i > 0 ; i--){
    ctx.beginPath();
    ctx.arc(200, 200, 20 * i, 0, Math.PI * 2, true);
    ctx.fillStyle = "rgb(" + random() + "," + random() + "," +random() + ")";
    ctx.fill();
    ctx.stroke();
}

绘制二次贝塞尔曲线及三次贝塞尔曲线

贝塞尔曲线

在绘制图形时,贝塞尔全非常有用,常用来绘制复杂的有规律的图形。

  • quadraticCurveTo(cpx, cpy, x, y)  
    绘制二次贝塞尔曲线,cpx,cpy表示控制点的坐标, x,y表示终点坐标;
  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
    绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。
Canvas(基础)_第3张图片

上边的图能够很好的描述两者的关系,贝塞尔曲线有一个开始、结束点(蓝色)以及一个控制点(红色),而三次贝塞尔曲线使用两个控制点。

参数x、y在这两个方法中都是结束点坐标。cp1x,cp1y为坐标中的第一个控制点,cp2x,cp2y为坐标中的第二个控制点。

二次贝塞尔曲线

quadraticCurveTo(cpx, cpy, x, y)  
cpx,cpy表示控制点的坐标, x,y表示终点坐标;

Canvas(基础)_第4张图片

P0 为起点, P2为终点, P1为控制点。

// 先简单绘制一个二次贝塞尔曲线
var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d');
//绘制起始点、控制点、终点  
context.beginPath();  
context.moveTo(20,170);  
context.lineTo(130,40);  
context.lineTo(180,150);    
context.stroke();            
//绘制二次贝塞尔曲线  
context.beginPath();  
context.moveTo(20,170);  
context.quadraticCurveTo(130,40,180,150); 
context.strokeStyle = "red"; 
context.stroke();   

效果如图:
Canvas(基础)_第5张图片

实例: 用二次贝塞尔曲线绘制对话气泡

// 二次贝尔赛曲线
ctx.beginPath();
ctx.moveTo(75,25);
ctx.quadraticCurveTo(25,25,25,62.5);
ctx.quadraticCurveTo(25,100,50,100);
ctx.quadraticCurveTo(50,120,30,125);
ctx.quadraticCurveTo(60,120,65,100);
ctx.quadraticCurveTo(125,100,125,62.5);
ctx.quadraticCurveTo(125,25,75,25);
ctx.stroke();

代码效果如下:


Canvas(基础)_第6张图片

三次贝塞尔曲线

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。

Canvas(基础)_第7张图片

P0 和 P3 为起点和终点, P1 和 P2 为控制点

先简单绘制一个三次贝塞尔曲线模型感受一下。

var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d'); 
//绘制起始点、控制点、终点 
context.beginPath(); 
context.moveTo(25,175); 
context.lineTo(60,80); 
context.lineTo(150,30); 
context.lineTo(170,150); 
context.stroke();
//绘制3次贝塞尔曲线 
context.beginPath(); 
context.moveTo(25,175); 
context.bezierCurveTo(60,80,150,30,170,150); 
context.strokeStyle = "red"; 
context.stroke(); 

效果图如下:


Canvas(基础)_第8张图片

实例:使用三次贝塞尔曲线绘制一个心形❤

 // 三次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(75,40);
ctx.bezierCurveTo(75,37,70,25,50,25);
ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
ctx.bezierCurveTo(20,80,40,102,75,120);
ctx.bezierCurveTo(110,102,130,80,130,62.5);
ctx.bezierCurveTo(130,62.5,130,25,100,25);
ctx.bezierCurveTo(85,25,75,37,75,40);
ctx.fill();

代码效果如下:

!
Canvas(基础)_第9张图片

绘制文本

canvas 提供了两种方法来渲染文本:

fillText(text, x, y [, maxWidth])
在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的.

strokeText(text, x, y [, maxWidth])
在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的

上代码:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

// 获取canvas的宽度和高度
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;

// 设置字体
ctx.font = "50px 宋体"
// 设置水平对其方式
// 水平对齐选项. 可选的值包括:start, end, left, right or center. 默认值是 start。
ctx.textAlign = "center";

// 设置垂直对齐方式
// 基线(垂直)对齐选项. 可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默认值是 alphabetic。
ctx.textBaseline = "middle"

// 绘制文字
ctx.storkeText("今天要是啊",100,100);

// 让文字水平垂直居中
ctx.font = "40px 楷体"
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("哈哈哈", canvasWidth/2, canvasHeight/2);

文本同样可以使用 strokeText() 和 fillText() 来绘制空心或实心文字,并且可以添加各种样式

使用图片

从零创建一个图片

我们可以用脚本创建一个新的Image对象。要实现这个方法,我们可以使用很方便的Image()构造函数。

var img = new Image();   // 创建一个元素
img.src = 'myImage.png'; // 设置图片源地址

当脚本执行后,图片开始装载。

若调用 drawImage 时,图片没装载完,那什么都不会发生(在一些旧的浏览器中可能会抛出异常)。因此你应该用load时间来保证不会在加载完毕之前使用这个图片:

var img = new Image();   // 创建img元素
img.src = 'myImage.png'; // 设置图片源地址
// 当图片加载完毕之后,才把图片绘制到canvas中
img.onload = function(){
  // 执行drawImage语句
  ctx.drawImage(img, 100, 100);
}
img.src = 'myImage.png'; // 设置图片源地址

使用视频帧

你还可以使用

function getMyVideo() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    return document.getElementById('myvideo');
  }
}

它将为这个视频返回HTMLVideoElement对象,抓取当前视频帧作为一个图像。

形变

旋转、位移、缩放

旋转: ctx.rotate(deg);
以坐标原点进行 deg 角度的旋转,可以为负值

位移: ctx.translate(x, y);
以坐标原点进行横向(x 像素),纵向(y 像素)的位移,可以为负值

缩放: ctx.scale(x, y);
以坐标原点进行横向(x 倍数),纵向(y 倍数)的缩放。

综合实例: 利用一个综合性的小实例进行学习(制作一个表盘)

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

// 获取canvas的宽度和高度
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;

// 将坐标原点移动至画布中心
ctx.translate(canvasWidth/2, canvasHeight/2);

// 设置线的两端为圆角
ctx.lineCap = "round";

// 设置线条宽度为10px
ctx.lineWidth = 10;

// 用foru循环绘制表盘的时间刻度
for(var i = 0; i < 12; i++) {
    ctx.beginPath();
    // 绘制一条长度为20的线
    ctx.moveTo(0,-200);
    ctx.lineTo(0, -180);
    ctx.stroke();
    // 绘制完成后将坐标原点旋转30度
    ctx.rotate(Math.PI / 6);
}

建议将代码拷贝到你自己的编辑器中多进行修改,可更快的了解每个方法和属性的作用。祝你学习愉快。

你可能感兴趣的:(Canvas(基础))