Canvas(基础)

标签:canvas 简单示例 入门知识
作者: 张耀国 ( IgorZhang )
E-mail: igorzhangcn@163.com


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

基本用法

<canvas id="canvas" width="500px" height="500px" >
    您的浏览器不支持canvas,请升级您的浏览器!
</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 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的正方形边框。

实现效果:


综合实例:绘制一个长宽为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为结束点。

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

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

二次贝塞尔曲线

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

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();   

效果如图:

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

// 二次贝尔赛曲线
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();

代码效果如下:


三次贝塞尔曲线

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

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(); 

效果图如下:


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

 // 三次贝塞尔曲线
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 提供了两种方法来渲染文本:

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>元素
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'; // 设置图片源地址

使用视频帧

你还可以使用<video> 中的视频帧(即便视频是不可见的)。例如,如果你有一个ID为“myvideo”的<video> 元素,你可以这样做:

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);
}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,552评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,666评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,519评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,180评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,205评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,344评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,781评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,449评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,635评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,467评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,515评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,217评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,775评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,851评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,084评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,637评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,204评论 2 341

推荐阅读更多精彩内容

  • 一、基础介绍和画直线 大多数现代浏览器都是支持Canvas的,比如 Firefox, safari, chrome...
    空谷悠阅读 806评论 0 1
  • 一:canvas简介 1.1什么是canvas? ①:canvas是HTML5提供的一种新标签 ②:HTML5 ...
    GreenHand1阅读 4,657评论 2 32
  • 请各位读者添加一下作者的微信公众号,以后有新的文章,将在微信公众号直接推送给各位,非常感谢。 0. 前言 相信各位...
    MR_LIXP阅读 5,149评论 17 56
  • 一、图形的组合方式 globalAlpha是一个介于0和1之间的值(包括0和1),用于指定所有绘制的透明度。默认值...
    空谷悠阅读 1,243评论 0 0
  • 东岳西麓有仙境, 素称泰山小江南。 清溪萦绕乃天宝, 彩石缤纷为物华。 临入石溪桃花峪, 石溪深处桃花源。 浑然天...
    风月无色阅读 207评论 0 0