<canvas> 元素

<canvas> 看起来和 <img> 元素很相像,唯一的不同就是它并没有 src 和 alt 属性。实际上,<canvas> 标签只有两个属性—— width和height。当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。该元素可以使用CSS来定义大小,但在绘制时图像会伸缩以适应它的框架尺寸:如果CSS的尺寸与初始画布的比例不一致,它会出现扭曲
<canvas> 元素有一个做 getContext() 的方法,这个方法是用来获得渲染上下文和它的绘画功能。
我们这里只做2d的绘画功能。

<html>
<head>
<title>Canvas tutorial</title>
<style type="text/css">
canvas { border: 1px solid black; }
</style>
<body onload="draw();">
<canvas id="tutorial" width="150" height="150">您的浏览器不支持canvas</canvas>
</body>
<script type="text/javascript">
function draw(){
var canvas = document.getElementById('tutorial');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
}
}
</script> </head> </html>

模板看起来会是这样。如这里所示,它最初是空白的。

一个简单例子
一开始,让我们来看个简单的例子,我们绘制了两个有趣的长方形,其中的一个有着alpha透明度。我们将在接下来的例子里深入探索一下这是如何工作的。

<html>
<head>
<script type="application/javascript">
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d"); ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50); ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, 55, 50);
}
}
</script>
</head>
<body onload="draw();">
<canvas id="canvas" width="150" height="150"></canvas>
</body>
</html>

效果

栅格

在我们开始画图之前,我们需要了解一下画布栅格(canvas grid)以及坐标空间。上一页中的HTML模板中有个宽150px, 高150px的canvas元素。如右图所示,canvas元素默认被网格所覆盖。通常来说网格中的一个单元相当于canvas元素中的一像素。栅格的起点为左上角(坐标为(0,0))。所有元素的位置都相对于原点定位。所以图中蓝色方形左上角的坐标为距离左边(Y轴)x像素,距离上边(X轴)y像素(坐标为(x,y))。在课程的最后我们会平移原点到不同的坐标上,旋转网格以及缩放。现在我们还是使用原来的设置。


HTML中的元素canvas只支持一种原生的图形绘制:矩形。所有其他的图形的绘制都至少需要生成一条路径。不过,我们拥有众多路径生成的方法让复杂图形的绘制成为了可能。

首先,我们回到矩形的绘制中。canvas提供了三种方法绘制矩形:
fillRect(x, y, width, height)
绘制一个填充的矩形
strokeRect(x, y, width, height)
绘制一个矩形的边框
clearRect(x, y, width, height)
清除指定矩形区域,让清除部分完全透明,例如一个镂空的矩形。
上面提供的方法之中每一个都包含了相同的参数。x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。width和height设置矩形的尺寸。

下面的draw() 函数是前面中取得的,现在就来使用上面的三个函数。

function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d'); ctx.fillRect(25,25,100,100);
ctx.clearRect(45,45,60,60);
ctx.strokeRect(50,50,50,50);
}
}

效果

fillRect()函数绘制了一个边长为100px的黑色正方形。clearRect()函数从正方形的中心开始擦除了一个6060px的正方形,接着strokeRect()在清除区域内生成一个5050的正方形边框。
绘制路径
图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。一个路径,甚至一个子路径,都是闭合的。使用路径绘制图形需要一些额外的步骤。

首先,你需要创建路径起始点。
然后你使用画图命令去画出路径。
之后你把路径封闭。
一旦路径生成,你就能通过描边或填充路径区域来渲染图形。
以下是所要用到的函数:
beginPath()
新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
closePath()
闭合路径之后图形绘制命令又重新指向到上下文中。
stroke()
通过线条来绘制图形轮廓。
fill()
通过填充路径的内容区域生成实心的图形。
生成路径:
第一步叫做beginPath()。本质上,路径是由很多子路径构成,这些子路径都是在一个列表中,所有的子路径(线、弧形、等等)构成图形。而每次这个方法调用之后,列表清空重置,然后我们就可以重新绘制新的图形。
第二步就是调用函数指定绘制路径,本文稍后我们就能看到了。

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

function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d'); ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(100,25);
ctx.fill();
}
}

效果

移动笔触

一个非常有用的函数,而这个函数实际上并不能画出任何东西,也是上面所描述的路径列表的一部分,这个函数就是moveTo()。或者你可以想象一下在纸上作业,一支钢笔或者铅笔的笔尖从一个点到另一个点的移动过程。

moveTo(x, y)
将笔触移动到指定的坐标x以及y上。
当canvas初始化或者beginPath()调用后,你通常会使用moveTo()函数设置起点。我们也能够使用moveTo()绘制一些不连续的路径。看一下下面的笑脸例子。我将用到moveTo()方法(红线处)的地方标记了。

你可以尝试一下,使用下边的代码片。只需要将其复制到之前的draw()函数即可。

function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d'); ctx.beginPath();
ctx.arc(75,75,50,0,Math.PI*2,true); // 绘制
ctx.moveTo(110,75);
ctx.arc(75,75,35,0,Math.PI,false); // 口(顺时针)
ctx.moveTo(65,65);
ctx.arc(60,65,5,0,Math.PI*2,true); // 左眼
ctx.moveTo(95,65);
ctx.arc(90,65,5,0,Math.PI*2,true); // 右眼
ctx.stroke();
}
}

效果


arc()函数介绍:


由于canvas中所有于角有关的API,都需要以弧度(R)来指定该角的值。三角函数也都采用弧度制。所以需要记好以下公式:
180度=π弧度
1弧度=(π/180)×度
1度=(180/π)×弧度
π=3.14,所以45度等于(3.14/180)×45度得0.7853弧度


如果你想看到连续的线,你可以移除调用的moveTo()。
线

绘制直线,需要用到的方法lineTo()。

lineTo(x, y)
绘制一条从当前位置到指定x以及y位置的直线。
该方法有两个参数:x以及y ,代表坐标系中直线结束的点。开始点和之前的绘制路径有关,之前路径的结束点就是接下来的开始点,等等。。。开始点也可以通过moveTo()函数改变。

下面的例子绘制两个三角形,一个是填充的,另一个是描边的。

function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d'); // 填充三角形
ctx.beginPath();
ctx.moveTo(25,25);
ctx.lineTo(105,25);
ctx.lineTo(25,105);
ctx.fill(); // 描边三角形
ctx.beginPath();
ctx.moveTo(125,125);
ctx.lineTo(125,45);
ctx.lineTo(45,125);
ctx.closePath();
ctx.stroke();
}
}

这里从调用beginPath()函数准备绘制一个新的形状路径开始。然后使用moveTo()函数移动到目标位置上。然后下面,两条线段绘制后构成三角形的两条边。


你会注意到填充与描边三角形步骤有所不同。正如上面所提到的,因为路径使用填充(filled)时,路径自动闭合,使用描边(stroked)则不会闭合路径。如果没有添加闭合路径closePath()到描述三角形函数中,则只绘制了两条线段,并不是一个完整的三角形。

圆弧

绘制圆弧或者圆,我们使用arc()方法。当然可以使用arcTo(),不过这个的现实并不是那么的可靠,所以我们这里不作介绍。

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时,是逆时针方向,否则顺时针方向。
下面的例子比上面的要复杂一下,下面绘制了12个不同的角度以及填充的圆弧。

下面两个for循环,生成圆弧的行列(x,y)坐标。每一段圆弧的开始都调用beginPath()。代码中,每个圆弧的参数都是可变的,实际生活中,我们并不需要这样做。

x,y坐标是可变的。半径(radius)和开始角度(startAngle)都是固定的。结束角度(endAngle)在第一列开始时是180度(半圆)然后每列增加90度。最后一列形成一个完整的圆。

clockwise 语句作用于第一、三行是顺时针的圆弧,anticlockwise作用于二、四行为逆时针圆弧。if 语句让一、二行描边圆弧,下面两行填充路径。

function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d'); for(var i=0;i<4;i++){
for(var j=0;j<3;j++){
ctx.beginPath();
var x = 25+j*50; // x 坐标值
var y = 25+i*50; // y 坐标值
var radius = 20; // 圆弧半径
var startAngle = 0; // 开始点
var endAngle = Math.PI+(Math.PI*j)/2; // 结束点
var anticlockwise = i%2==0 ? false : true; // 顺时针或逆时针 ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise); if (i>1){
ctx.fill();
} else {
ctx.stroke();
}
}
}
}
}

效果


贝塞尔(bezier)以及二次贝塞尔

下一个十分有用的路径类型就是 贝塞尔曲线。二次以及三次贝塞尔曲线都十分有用,一般用来绘制复杂有规律的图形。

quadraticCurveTo(cp1x, cp1y, x, y)
绘制二次贝塞尔曲线,x,y为结束点,cp1x,cp1y为控制点。
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
绘制三次贝塞尔曲线,x,y为结束点,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二。
下边的图能够很好的描述两者的关系,二次贝塞尔曲线有一个开始、结束点(蓝色)以及一个控制点(红色),而三次贝塞尔曲线使用两个控制点。

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

使用二次以及三次贝塞尔曲线是有一定的难度的,因为不同于像Adobe Illustrators这样的矢量软件,我们所绘制的曲线没有直接的视觉反馈给我们。这让绘制复杂的图形十分的困难。在下面的例子中,我们会绘制一些简单有规律的图形,如果你有时间,以及更多的耐心很多复杂的图形你都可以绘制出来。


可以看我自己写的一个实际项目例子,在我的文章里有。
今天就先写到这了。后面再慢慢给大家写,我也是刚刚学这个。不足之处多谅解。关注我哦^_^!

小白上学のcanvas零基础的更多相关文章

  1. 学Java必看!零基础小白再也不用退缩了

    程序员们!请往这儿看 对于JAVA的学习,可能你还会有许多的顾虑 不要担心 接着往下看吧 学Java前 一.数学差,英语也不好是不是学不好Java? 答案是:是~ 因为你在问这个问题的时候说明你对自己 ...

  2. 零基础小白怎么用Python做表格?

    用Python操作Excel在工作中还是挺常用的,因为毕竟不懂Excel是一个用户庞大的数据管理软件.本文用Python3!在给大家分享之前呢,小编推荐一下一个挺不错的交流宝地,里面都是一群热爱并在学 ...

  3. 【自动化基础】手把手教零基础小白搭建APP的UI自动化环境

    前言 帮助零基础小白一步步搭建UI自动化环境,完成Python+Appium+模拟器/真机的UI自动化环境搭建. 环境准备: jdk1.8.0 sdk Node.js appium python Ap ...

  4. 零基础如何迅速学习HTML5?新手小白学习web前端H5自白!

    很多的人在毕业之后才发现原来学的专业不是自己想做的工作,或者专业对口的工作待遇让人觉得并不满意,于是很多人选择培训机构学新的一门技能转换行业.IT行业的web前端H5受到很多学员的青睐.那么学习web ...

  5. 快快使用ModelArts,零基础小白也能玩转AI!

    摘要: 走过路过不要错过,看Copy攻城狮如何借力华为云ModelArts玩转AI. "自2018年10月发布以来,ModelArts累计服务了众多行业十几万开发者,通过基础平台的完备性和面 ...

  6. 零基础的Java小白如何准备初级开发的面试

    对于各位Java程序员来说,只要能有实践的机会,哪怕工资再低,公司情况再一般,只要自己上心努力,就可能在短时间内快速提升,甚至在工作2年后进大厂都有希望,因为项目里真实的开发实践环境是平时学习不能模拟 ...

  7. ​零基础该如何学习UI设计

    ​零基础学习该如何学习UI设计,没有基础该怎么开始学习呢?UI设计可以说是入行门槛很低的职业了,而且随着互联网的快速发展,UI设计的市场前景也越来也好,更多的人看到了这个高薪的行业也开始心动了,想要在 ...

  8. HTML5零基础学习Web前端需要知道哪些?

    HTML零基础学习Web前端网页制作,首先是要掌握一些常用标签的使用和他们的各个属性,常用的标签我总结了一下有以下这些: html:页面的根元素. head:页面的头部标签,是所有头部元素的容器. b ...

  9. 还在花钱搞开发?猿团YTFCloud,零基础照样做专业APP

    近日,猿团科技再推新品:YTFCloud.这是一套一体化的云端解决方案,用户可以通过平台提供的各类解决方案,一键创建应用,也就是说,YTFCloud实现了APP的DIY自制,用户无需懂得编程,零基础制 ...

随机推荐

  1. JZ-045-扑克牌顺子

    扑克牌顺子 题目描述 LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的 ...

  2. Python 细聊可以媲美 PS 的 PIL 图片处理库

    1 . 前言 PIL 是 Python Image Library 的简称. PIL 库中提供了诸多用来处理图片的模块,可以对图片做类似于 PS(Photoshop) 的编辑.比如:改变图像大小.旋转 ...

  3. GO语言基础(结构+语法+类型+变量)

    GO语言基础(结构+语法+类型+变量) Go语言结构 Go语言语法 Go语言类型 Go语言变量       Go 语言结构 Go 语言的基础组成有以下几个部分: 包声明 引入包 函数 变量 语句 &a ...

  4. Java集合多线程安全

    线程安全与不安全集合 线程不安全集合: ArrayList LinkedList HashMap HashSet TreeMap TreeSet StringBulider 线程安全集合: Vecto ...

  5. SpringBoot 如何实现异步编程,老鸟们都这么玩的!

    镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 首先我们来看看在Spring中为什么要使用异步编程,它能解决什么问题? 为什么要用异步框架,它解决什么问题? 在SpringBoot的日常开发中 ...

  6. JSON.parse()和JSON.stringfy()区别

    JSON.parse() 用于从一个json格式字符串解析出json类型的数据,如: 注意事项:json格式字符串必须是写在一排的,且括号外面用单引号,里面的每一个字符串用双引号 JSON.strin ...

  7. KVM 虚拟化基本知识,virtio工作原理

    KVM虚拟化的基本知识,virtio的工作流程及原理,virtio-vhost, virtio-vhost-user pci 配置空间,是谁在kick 写pci配置空间的?又是通过什么机制通知给qem ...

  8. 【模板】动态 DP

    luogu传送门. 最近学了一下动态dp,感觉没有想象的难. 动态DP simple的DP是这样的: 给棵树,每个点给个权值,求一下最大权独立集. 动态DP是这样的: 给棵树,每个点给个权值还到处改, ...

  9. SP接口的全双工首发接口整合

    unsigned char bits = 8; unsigned int speed = 50000; unsigned short delay; static void spi_transfer_d ...

  10. buu equation wp

    知识点考察:jsfuck解码.js逆向.z3处理大量数据 源码分析 源码 根据提示猜测有jsfuck Jsfuck编码共六个字符分别为[.].+.!.(.) 观察上述不难发现l['jsfuck']=' ...