一、canvas的介绍

  canvas是html5出现的新标签,像所有的DOM对象一样它有自己本身 的属性、方法和事件,其中就有绘图的方法,js能够调用它来进行绘图。canvas只有两个属性,而且是可选的,width、height,这两个属性也可以通过 js 来定义。

  canvas如果没有定义大小,则默认大小为宽 300px,高 150px。当然使用 css 也是可以设置 canvas 的大小,但如果css设置的宽高比例与 js 或在 标签的属性 width、height上设置的比例不一致,则会变形,而且如果css设置的值是它们的n倍,则图像也会放大n倍。

如下图第一个是长宽50的红色矩形,第二个是css设置为js设置的两倍是得出的图像,第三个是css设置宽高比为1.5:1时得出的图像且高是js设置的两倍。

  canvas在某些较老的浏览器(如IE9以下)中并不支持,我们可以在canvas标签中写些内容替代当canvas标签不被支持的时候。也可以使用 getContext 来判断是否支持canvas。

<!-- 插入替换的图片或提示文本 -->
<canvas id="canvas" width="500" height="500">
<img src="data:images/canvas.png" width="500" height="500" alt=""/> //
</canvas>
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
console.log('你的浏览器支持Canvas!');
} else {
console.log('你的浏览器不支持Canvas!');
}

  canvas.getContext() 方法返回了一个相当于画笔的“东西”,通过它我们可以进行各种绘画。当然绘画时也会考虑是画2D的还是3D的,这个方法在传入参数后即可,如下:

var context = canvas.getContext("2d");

如果想画3D的该怎么办,这样:

var context = canvas.getContext("webgl");

本文只讨论2D的绘画的使用。

  画图首先要有一个参考系,原点在哪,x、y轴的方向,单位是什么?

如下图,坐标轴在左上角,x轴为横向向右,y轴为垂直向下,所以画图的坐标都是正值,如果是负值,图像就超出画布看不见了。坐标轴单位为px。

二、canvas绘制矩形

  画矩形首先要知道在哪里画,矩形的大小,边框的粗细,边框的颜色,是否是填充的这些要素。而我们的画笔的API可以做到这些。

  context.strokeRect(10,10,50,50),绘制无填充的矩形,四个参数分别表示:x轴坐标,y轴坐标,长,宽。。

  context.fillRect(10,10,50,50),绘制填充的矩形,默认填充黑色。

var canvas = document.getElementById("canvas");
canvas.width="500";
canvas.height="500"; //第一步必须获取一个"画笔"
var context = canvas.getContext("2d"); //画矩形
//设置边框宽度  单位px
context.lineWidth="6";
//设置颜色
context.strokeStyle="red";
context.strokeRect(10,10,50,50)
context.fillStyle = '#f00'
context.fillRect(10,10,20,20) //清除画笔的轨迹  将所在区域的矩形内容清空可以理解为橡皮擦
context.clearRect(15,15,20,20)

画出图像如下,左上角白色的就是清除掉的部分。

如果你设置了边框宽度与颜色,但画出的图像没有,则是要在画之前进行设置,就是在context.strokeRect(10,10,100,100)之前定义,在其之后定义就只对后面画的有效了。

  还有一个方法可以绘画矩形:这两个API要永远在一起,分开的就无法绘画了。

context.rect(10,10,100,100);
//绘制的形状
context.stroke()

三、canvas绘制圆和弧线

  context.arc(150, 150, 100, 0, 2*Math.PI, false),绘制圆,六个参数分别是:圆心的x轴坐标,圆心的y轴坐标,半径,圆弧的起始弧度,圆的结束弧度,绘制的方向(默认为false,顺时针方向)。圆弧的起始弧度默认为水平方向直径与圆弧的右边交点处,如下图A点处,结束位置是顺时针方向的弧度结束点。360°是2Π,90°是1/2Π。

  如果要绘制实心的圆则要用 context.fill() 表示绘制实心圆,这是你可以不写 context.stroke() 那么圆就没有边框。如果绘制的弧度小于2Π那绘制实心的时候出来的是一个月亮一样的图形。绘制图像如下图右边两张。故如果要绘制弧线则让所绘制的的弧度之差小于2Π即可。

context.arc(150,150,100,0,2*Math.PI,false);
context.strokeStyle="#c33"; //边框颜色
context.fillStyle="red"; //填充颜色
// context.fill(); //填充
context.stroke(); //画边框 context.arc(150,150,100,.5*Math.PI,1*Math.PI); // 弧度从1/2*Π 到 Π,及90°到180°
context.strokeStyle="#c33";
// context.fill();
context.stroke();

  

四、canvas绘制线

  context.moveTo(10,50),将“画笔”移动到指定的坐标x以及y上,context.lineTo()绘制路径的结束点。可以设置路径的粗细与颜色,粗细的单位是px。

// 绘制一条线
context.moveTo(10,150);
context.lineTo(350,150);
context.strokeStyle="black";
context.lineWidth="6";
context.stroke();

  所的图像如下,是一条长340px的直线。

  这时自然会想到如果同时绘制多条直线与弧会怎样,如下代码:

context.moveTo(10,150);
context.lineTo(350,150);
context.strokeStyle="black";
context.lineWidth="30";
context.stroke(); context.moveTo(50,50);
context.lineTo(200,200);
context.strokeStyle="green";
context.lineWidth="10";
context.stroke(); context.arc(150,150,100,0.5*Math.PI,1.5*Math.PI,false);
context.strokeStyle="#c33";
context.lineWidth="2";
context.stroke();

  所得如下图像,可以看到图中右很多线,但我们只画了三条。一条是中间的横线粗30px,黑色;一条是斜线粗10px,绿色;一条是一个粗1px红色的半圆。图上看起来好像有7条线。这是由于在canvas的绘制中,直线是由路径进行绘制的。

下面的痕迹也可以理解为你拿着画笔,笔一直没有离开画布画了这三条线。在不同的线之间移动时画笔也会留下痕迹,故多了那些多余的痕迹。

五、什么是路径?

  图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。canvas只支持原生绘制矩形,其他的图形绘制都至少需要一条生成路径。

使用路径绘制图形需要一些额外的步骤。

  1. 你需要创建路径起始点。
  2. 然后你使用“画笔”去画出路径。
  3. 把路径封闭。
  4. 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。

以下是所要用到的函数:

  1. beginPath()   新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
  2. closePath()   闭合路径之后图形绘制命令又重新指向到上下文中。
  3. stroke()     通过线条来绘制图形轮廓。
  4. fill()      通过填充路径的内容区域生成实心的图形。

  调用beginPath()之后,或者canvas刚建的时候,第一条路径构造命令通常被视为是moveTo()。因为你几乎总是要在设置路径的起始位置。生成路径的步骤:

第一步叫做beginPath()。本质上,路径是由很多子路径构成,这些子路径都是在一个列表中,所有的子路径(线、弧形、等等)构成图形。而每次这个方法调用之后,列表清空重置,然后我们就可以重新绘制新的图形。

第二步就是调用函数指定绘制路径,本文稍后我们就能看到了。

第三,就是闭合路径closePath(),不是必需的。这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。

  当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合。
 
将上面的代码闭合一下:
ctx.beginPath();
context.moveTo(10,150);
context.lineTo(350,150);
context.strokeStyle="black";
context.lineWidth="30";
context.stroke();
ctx.closePath(); ctx.beginPath();
context.moveTo(50,50);
context.lineTo(200,200);
context.strokeStyle="green";
context.lineWidth="10";
context.stroke();
ctx.closePath(); ctx.beginPath();
context.arc(150,150,100,0.5*Math.PI,1.5*Math.PI,true);
context.strokeStyle="#c33";
context.lineWidth="2";
context.stroke();
ctx.closePath();

所得图像如下:只有三条线了,没有其他的痕迹了。有人会说为啥半圆的方向变了,细心的朋友肯定发现了,其实是arc()的第六个参数改为 true导致圆弧绘制时的方向改变的缘故。

六、函数回调式的写法

  有时经常书写画笔的开始、闭合会很麻烦,这是会想可不可以将这些重复的代码封装以下,以后直接调用就行。没错就是这样,这是可以滴。如下:

function draw(cb){
context.beginPath();
context.save();
cb();
context.restore();
context.closePath();
} draw(()=>{
context.moveTo(10,150);
context.lineTo(350,150);
context.strokeStyle="black";
context.lineWidth="30";
context.stroke();
}) draw(()=>{
context.moveTo(50,50);
context.lineTo(200,200);
context.strokeStyle="green";
context.lineWidth="10";
context.stroke();
}) draw(()=>{
context.arc(150,150,100,0.5*pi,1.5*pi,true);
context.strokeStyle="#c33";
context.lineWidth="2";
context.stroke();
})

如上代码画出的图像和上面的图是一样的,有人问这样写有什么用,这样写在画复杂图形时节省了起始与闭合代码的重复书写,也很清晰的能看出那些是一个画笔画出来的。

这是会有小伙伴问draw函数内的 save()、restore()是干什么的。

七、save()、restore()

  用于保存及恢复当前Canvas绘图环境的所有属性。其中save()可以保存当前状态,而restore()可以还原之前保存的状态。能起到保存绘制状态和防止污染状态栈。

  可以用栈来进行理解,当调用save()时会将“画笔”的状态(填充风格、线条风格、阴影风格的各种绘图状态)压入一个状态栈中,每调用一次就压入一次。当要使用时,用restore()来获取保存 的“画笔”状态,遵循栈的读取规则,先进后出,你最先取出的是你最后压入栈中的。如果你调用restore()的次数比save()多,那多余的次数画出的都是无效的。

  save()只是保存了状态,并没有保留之前绘制的图形。

ctx.fillStyle = 'red';
ctx.fillRect(10,30,15,15);
ctx.save(); // 将第一个状态压入栈中 ctx.fillStyle = 'blue';
ctx.fillRect(50,20,30,30);
ctx.save(); // 将第二个状态压入栈中 ctx.fillStyle = 'green';
ctx.fillRect(100,10,50,50);
ctx.save(); // 将第三个状态压入栈中 ctx.restore(); // 取出栈3(第三个状态)
ctx.beginPath();
ctx.arc(200, 35, 30, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill(); ctx.restore(); // 取出栈2(第二个状态)
ctx.beginPath();
ctx.arc(300, 35, 20, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill(); ctx.restore(); // 取出栈1(第一个状态)
ctx.beginPath();
ctx.arc(400, 35, 10, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill(); ctx.restore(); // 取出栈1(第一个状态)
ctx.beginPath();
ctx.arc(400, 35, 10, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();

看出最后多余的一次读取状态所绘图形并没有显示出来。所以读取次数要少于压入次数。否则无效。

八、绘制文字

  文字的绘制很简单,可以设置字体,大小,颜色,位置。

  字体的位置 第一个number参数时x轴坐标,第二number个为y轴坐标,字体的原点在字体的左下角,如下图所示位置。

context.strokeStyle="#c33"; //设置颜色,如果要设置实心的字体颜色则要用 fillStyle
context.font="40px 微软雅黑"; //设置大小与字体
context.fillText("wertantan的博客园",80,80); //实心字体,设置所绘字体内容与位置
context.strokeText("wertantan的博客园",80,150); //空心字体

九、绘制贝塞尔曲线

  二次贝塞尔曲线与三次贝塞尔曲线一般用来绘制复杂有规律的图形。可以用贝塞尔曲线组合直线、填充与矩形来绘制复杂的图形。

  quadraticCurveTo(cp1x, cp1y, x, y) 绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,x,y为结束点。

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

  有人会问起始点去哪了,起始点可以通过moveTo()来定义,直接移动画笔到你所要绘制的起始点,之后再绘制贝塞尔曲线的控制点与结束点。每次绘制贝塞尔曲线都可以用moveTo()来定义起始点的位置。

  二次贝塞尔曲线有一个起始点(蓝色)、一个结束点(蓝色)以及一个控制点(红色),而三次贝塞尔曲线有两个控制点。

// 二次贝塞尔曲线
context.beginPath();
context.moveTo(50,50);
context.quadraticCurveTo(200,160,50,200);
context.stroke();
context.closePath(); // 三次贝塞尔曲线
context.beginPath();
context.moveTo(50,50);
context.bezierCurveTo(200,80,200,170,50,200)
context.stroke();
context.closePath();

所绘制图像如下:

       

Canvas除了能绘制基本的形状和文本,还可以实现动画、缩放、各种滤镜和像素转换等高级操作。这里就不再说了,感兴趣的小伙伴可以去 MDN 看一看。

至此canvas的基础使用就结束了,希望各位能有所收获,如有问题还望指正!

本文所有图片均由canvas绘制,截取下来。

这里附上一个小案例,是一个方形从浏览器可视区的左上角移动到右下角的动画,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
*{margin:0;padding:0;}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>
<script>
var canvas = document.getElementById("canvas");
var iw = document.documentElement.clientWidth;
var ih = document.documentElement.clientHeight;
var context = canvas.getContext("2d"); canvas.width = iw-50;
canvas.height= ih-50;
var x = 0;
var y = 0;
var b = (ih-50)/(iw-50); var timer = setInterval(function(){
context.clearRect(0,0,iw,ih);
x++;
y+=b;
context.fillRect(x,y,50,50*b);
if(x>=iw-100){
clearInterval(timer);
}
console.log(x)
},30)
</script>

效果如下:

转载请注明出处!

canvas 的基本使用的更多相关文章

  1. html5 canvas常用api总结(三)--图像变换API

    canvas的图像变换api,可以帮助我们更加方便的绘画出一些酷炫的效果,也可以用来制作动画.接下来将总结一下canvas的变换方法,文末有一个例子来更加深刻的了解和利用这几个api. 1.画布旋转a ...

  2. 【探索】利用 canvas 实现数据压缩

    前言 HTTP 支持 GZip 压缩,可节省不少传输资源.但遗憾的是,只有下载才有,上传并不支持.如果上传也能压缩,那就完美了.特别适合大量文本提交的场合,比如博客园,就是很好的例子. 虽然标准不支持 ...

  3. 简单入门canvas - 通过刮奖效果来学习

    一 .前言 一直在做PC端的前端开发,从互联网到行业软件.最近发现移动端已经成为前端必备技能了,真是不能停止学习.HTML5新增的一些东西,canvas是用的比较多也比较复杂的一个,简单的入门了一下, ...

  4. 获取Canvas当前坐标系矩阵

    前言 在我的另一篇博文 Canvas坐标系转换 中,我们知道了所有的平移缩放旋转操作都会影响到画布坐标系.那在我们对画布进行了一系列操作之后,怎么再知道当前矩阵数据状态呢. 具体代码 首先请看下面的一 ...

  5. Canvas坐标系转换

    默认坐标系与当前坐标系 canvas中的坐标是从左上角开始的,x轴沿着水平方向(按像素)向右延伸,y轴沿垂直方向向下延伸.左上角坐标为x=0,y=0的点称作原点.在默认坐标系中,每一个点的坐标都是直接 ...

  6. Canvas绘图之平移translate、旋转rotate、缩放scale

    画布操作介绍 画布绘图的环境通过translate(),scale(),rotate(), setTransform()和transform()来改变,它们会对画布的变换矩阵产生影响. 函数 方法 描 ...

  7. 用html5的canvas和JavaScript创建一个绘图程序

    本文将引导你使用canvas和JavaScript创建一个简单的绘图程序. 创建canvas元素 首先准备容器Canvas元素,接下来所有的事情都会在JavaScript里面. <canvas ...

  8. html5标签canvas函数drawImage使用方法

    html5中标签canvas,函数drawImage(): 使用drawImage()方法绘制图像.绘图环境提供了该方法的三个不同版本.参数传递三种形式: drawImage(image,x,y):在 ...

  9. 使用 JavaScript 和 canvas 做精确的像素碰撞检测

    原文地址:Pixel accurate collision detection with Javascript and Canvas 译者:nzbin 我正在开发一个需要再次使用碰撞检测的游戏.我通常 ...

  10. H5项目开发分享——用Canvas合成文字

    以前曾用Canvas合成.裁剪.图片等<用H5中的Canvas等技术制作海报>.这次用Canvas来画文字. 下图中"老王考到驾照后"这几个字是画在Canvas上的,与 ...

随机推荐

  1. Openstack中用keypair生成和访问虚机的方法

    Openstack中用keypair生成和访问虚机的方法 标签:task   iso   perm   cte   生成   复制   vol   rsa   sla Openstack中用镜像文件生 ...

  2. 安装win10体验

    没事干了,心血来潮弄了个win10专业版. 讲硬盘重新分区了,没办法,原来分的太少了. 使用winpe启动,直接将下载的win10还原到c盘,成功启动,设置的时候让提示输入id,没有啊?研究发现可以先 ...

  3. Git实战

    Git实战 1.Git特点 1.1.Git两大特点 版本控制:可以解决多人同时开发的代码问题,也可以解决找回历史代码的问题. 分布式:Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器 ...

  4. 使用jquery删除链接所在的行

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  5. Docker 入门及安装[Docker 系列-1]

    docker 如日中天,这不是单纯的炒概念,docker 确确实实解决了开发与运维的痛点,因此在企业开发中得到了非常广泛的使用,本文对于 docker 的这些基本知识点再做一些简单回顾. 什么是 do ...

  6. JS面向对象编程(一):封装

    js是一门基于面向对象编程的语言.      如果我们要把(属性)和(方法)封装成一个对象,甚至要从原型对象生成一个实例,我们应该怎么做呢?  一.生成对象的原始模式            假定把猫看 ...

  7. Ubuntu+VMWare 学习中遇到的问题

    1. 虚拟机中Ubuntu分辨率 / 设置分辨率出现Unknown Display VMware中Ubuntu 出现Unknown Display问题解决 1.1 命令无法保存分辨率设置: xrand ...

  8. Linux基础文件查找

    一.文件查找 (一).命令文件 [root@linux ~]# chich ls //从PATH环境变量 [root@linux ~]# chereis vim [root@linux ~]# ech ...

  9. UR机器人通信--上位机通信(python)

    一.通信socket socket()函数 Python 中,我们用 socket()函数来创建套接字,语法格式如下: socket.socket([family[, type[, proto]]]) ...

  10. 【Java例题】8.2 手工编写字符串统计的可视化程序

      2. 手工编写字符串统计的可视化程序. 一个Frame窗体容器,布局为null,两个TextField组件,一个Button组件. Button组件上添加ActionEvent事件监听器Actio ...