大家好,我是石璞东。

在我书的第六章中有一个关于MNIST手写数字的例子,当数据集加载完成之后,用户可以在<canvas/>上输入手写数字,点击「预测」按钮之后,浏览器会弹出经模型预测之后的结果;在我书的第九章和第十章中,分别有关于目标检测和人体姿态检测的案例,当关键点的得分符合一定要求时,会通过<canvas/>将关键部分绘制出来,请看效果:

图1 - MNIST手写数字案例

图2 - 人体姿态检测案例

图3 - 目标检测案例

接下来,我们将在本文中详细讲解一下上述三个模块均用到的一个技术<canvas/>

参考资料

  1. MDN Web Docs:https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API
  2. 《JavaScript高级程序设计(第三版)》
  3. 《HTML5权威指南》

1.canvas概述

HTML5最受欢迎的功能就是<canvas>元素,Canvas API提供了一个通过JavaScriptHTML<canvas>元素来绘制图形的方式,可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面,该API主要聚焦于2D图形,我们也可以使用WebGL API绘制硬件加速的2D3D图形。

2.基本用法

使用<canvas>大概可以分为两个步骤:

  1. HTML中定义<canvas>标签;
  2. 获取<canvas>对象的上下文并判断getContext()方法是否存在;

了解了使用<canvas>的大概步骤之后,我们首先在页面中定义一个<canvas>标签,请看代码示例:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>canvas示例</title>
</head>
<body>
<canvas width="500" height="500" style="border: 1px solid red" id="canvas"></canvas>
</body>
</html>

该标签有两个属性——widthheight,当没有设置widthheight的值时,<canvas>标签会初始化大小为300*150px的矩形,我们也可以通过CSS去设置<canvas>元素的大小,但在绘制时图像会伸缩以适应它的尺寸框架,可能会出现扭曲等情况,所以推荐大家使用widthheight属性明确的规定宽度和高度,接下来,请看效果展示:

图4 - 画布展示

在页面中定义了<canvas>标签之后,我们需要获取该标签的绘图上下文,请看示例代码:

var canvas = document.getElementById("canvas")
if (canvas.getContext){var ctx = canvas.getContext("2d");ctx.fillStyle = "orange"ctx.fillRect(10,10,100,100)
}

上述代码中,我们首先通过 document.getElementById()方法获取页面中的<canvas>标签,并通过getContext()方法获取渲染上下文和它的绘画功能,接着我们通过ctx.fillStyle()ctx.fillRect()方法在<canvas>中绘制了一个左上角(xy)坐标为(10,10)且大小为100*100px的绿色正方形,请看效果展示:

图5 - 绘制矩形

3.2D上下文

使用2D绘图上下文提供的方法可以绘制简单的2D图形,比如矩形、弧线和路径。2D上下文的坐标开始于<canvas>元素的左上角,原点坐标是(0,0),所有坐标值都是基于这个原点计算,x值越大表示越靠右,y值越大表示越靠下。

图6 - canvas绘制规则

3.1 填充和描边

2D上下文的两种基本绘图操作是填充和描边。填充就是用指定的样式(颜色、渐变或图像)填充图形;描边就是只在图形的边缘画线。大多数2D上下文操作都会细分为填充和描边两个操作,其操作结果取决于fillStyle属性和strokeStyle属性,其默认值均为#000000,请看代码示例:

var canvas = document.getElementById("canvas")
if (canvas.getContext){var ctx = canvas.getContext("2d");ctx.fillRect(10,10,100,100)ctx.strokeRect(120,10,100,100)}

上述代码中,我们通过ctx.fillRect()方法和ctx.strokeRect()方法分别绘制了两个矩形,其中通过ctx.fillRect()方法绘制的为填充矩形;通过ctx.strokeRect()方法绘制的为描边矩形,其样式均采用默认样式,请看演示效果:

图7 - 案例展示

3.2 绘制矩形

canvas只支持两种形式的图形绘制:矩形和路径,矩形是唯一一种可以直接在2D上下文中绘制的形状,它提供了三种方法绘制矩形,请看详细介绍:

1.fillRext(x,y,width,height)

该方法用于绘制一个填充的矩形,可以通过fillStyle属性决定当前矩形的填充样式;

2.strokeRect(x,y,width,height)

该方法用于绘制一个矩形的边框;

3.clearRect(x,y,width,height)

该方法用于清除指定矩形区域,让清除部分完全透明;

上述提供的三个方法中均包含相同的参数,xy指定了在<canvas>画布上所绘制矩形左上角(相对于原点)的坐标,widthheight设置矩形的尺寸,请看演示案例:

var canvas = document.getElementById("canvas")
if (canvas.getContext){var ctx = canvas.getContext("2d");ctx.fillRect(100,100,300,300)ctx.clearRect(150,150,200,200)ctx.strokeRect(200,200,100,100)
}

上述代码中,我们在大小为500*500px的画布中绘制了三个矩形,其中第一个矩形左上角坐标为(100,100),大小为300*300;第二个矩形左上角坐标为(150,150),大小为200*200;第三个矩形左上角坐标为(200,200),大小为100*100,请看效果演示:

图8 - 案例展示

3.3 绘制路径

要绘制路径,首先必须调用beginPath()方法,表示要开始绘制新的路径,然后调用以下方法来绘制路径,请看方法介绍:

1.arc(x,y,radius,startAngle,endAngle,anticlockwise)

该方法会绘制一个以(x,y)为圆心,以radius为半径的圆弧,从startAngle开始到endAngle结束,其中,startAngle表示圆弧的起始点(单位:弧度),endAngle表示圆弧的终点(单位:弧度),我们还可以指定anticlockwise的参数值来规定绘制圆弧的方向(默认为顺时针);

2.arcTo(x1,y1,x2,y2,radius)

该方法根据给定的控制点(其中x1,y1表示第一个控制点的坐标,x2,y2表示第二个控制点的坐标,)和半径画一段圆弧,再以直线连接两个控制点;

3.lineTo(x,y)

该方法绘制一条从当前位置到指定x以及y位置的直线;

4.moveTo(x,y)

该方法将笔触移动到指定的坐标x以及y上,当<canvas>初始化或者beginPath()调用之后,我们通常会使用moveTo()函数设置起点;

5.rect(x,y,width,height)

该方法绘制一个左上角坐标为(x,y),宽高为widthheight的矩形;

创建了路径之后,我们可以调用closePath()方法绘制一条连接到路径起点的线条;也可以调用fill()方法并指定fillStyle属性完成填充;另外,也可以通过stroke()方法对路径描边,并通过strokeStyle指定其样式;

接下来,请看演示案例:

var canvas = document.getElementById("canvas")
if (canvas.getContext){var ctx = canvas.getContext("2d");//绘制一个圆形ctx.beginPath()ctx.moveTo(250,150)ctx.arc(250,150,10,0,2*Math.PI)ctx.fillStyle = "aqua"ctx.fill()ctx.closePath()//绘制一条线ctx.beginPath()ctx.moveTo(150,200)ctx.lineTo(350,200)ctx.lineWidth = 10ctx.strokeStyle = "blue"ctx.stroke()ctx.closePath()//绘制一个矩形ctx.beginPath()ctx.moveTo(150,250)ctx.rect(150,250,200,200)ctx.fillStyle = "green"ctx.fill()ctx.closePath()
}

上述代码中我们分别绘制了圆形、直线、矩形,请看效果展示:

图9 - 案例展示

3.4 绘制文本

canvas提供了两种方法来渲染文本,请看详细介绍:

1.fillText(text,x,y,[,maxWidth])

该方法在指定的(x,y)位置填充指定的文本,其中maxWidth表示绘制的最大宽度,为可选参数;

2.strokeText(text,x,y,[,maxWidth])

该方法在指定的(x,y)位置绘制文本边框,其中maxWidth表示绘制的最大宽度,为可选参数;

我们可以通过设置fonttextAligntextBaselinedirection的值来对文本进行渲染,请看详细介绍:

  • font:表示文本样式、大小及字体,默认字体是10px sans-serif

  • textAlign:表示文本对齐方式,其值包括left(文本左对齐)、right(文本右对齐)、center(文本居中对齐)、start(文本对齐界线开始的地方,即左对齐指本地从左向右,右对齐指本地从右向左)、end(文本对齐界线结束的地方,即左对齐指本地从左向右,右对齐指本地从右向左),默认值为start

  • textBaseline:表示文本的基线,其值包括top(文本基线在文本块的顶部)、hanging(文本基线是悬挂基线)、middle(文本基线在文本块的中间)、alphabetic(文本基线是标准的字母基线)、ideographicbottom,默认值是alphabetic

  • direction:此功能某些浏览器尚在开发中,请参考浏览器兼容性表格以得到在不同浏览器中适合使用的前缀;

接下来,请看演示案例:

var canvas = document.getElementById("canvas")if (canvas.getContext){var ctx = canvas.getContext("2d");ctx.font = "bold 30px Arial"ctx.textAlign = "center"ctx.textBaseline = 'middle'ctx.fillText("石璞东",100,100)ctx.strokeText("石璞东",200,100)}

上述代码中我们通过ctx.fillText()方法和ctx.strokeText()方法绘制了字符串石璞东,并指定了其字体样式、对齐方式等,请看效果演示:

图10 - 案例展示

3.5 绘制图像

可以用drawImage()方法在画布上绘制图像,该方法需要三个、五个或九个参数,请看参数解释:

1.drawImage(image, x, y)

其中,imageimage或者canvas对象,xy是其在目标canvas里的起始坐标;

2.drawImage(image, x, y, width, height)

该方法多了两个参数:widthheight,这两个参数用来控制当向canvas画入时应该缩放的大小;

3.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

第一个参数和其它的是相同的,都是一个图像或者另一个canvas的引用,前4个是定义图像源的切片位置和大小,后4个则是定义切片的目标显示位置和大小,如图所示:

图11 - 图解参数

接下来,请看演示案例:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>canvas示例</title><style>img{width: 250px;height: 250px;}</style>
</head>
<body>
<img src="author.jpg" id="image">
<button id="btn">绘制图像</button>
<button id="clear">清除画布</button>
<canvas width="500" height="500" style="border: 1px solid red" id="canvas"></canvas>
<script>var canvas = document.getElementById("canvas")var imgEle = document.getElementById("image")var oclear = document.getElementById("clear")var obtn = document.getElementById("btn")var num = 0;if (canvas.getContext){var ctx = canvas.getContext("2d");obtn.onclick = () => {if(num == 0){ctx.drawImage(imgEle,20,20)}else if (num == 1){ctx.drawImage(imgEle,20,20,100,100)}else{ctx.drawImage(imgEle,20,20,300,300,10,10,200,200)}}oclear.onclick = () => {ctx.clearRect(0,0,500,500)num >= 2 ? num=0:num++;}}
</script>
</body>
</html>

上述代码中,我们分别指定了当ctx.drawImage()方法分别有3个、5个、9个参数时绘制图像的效果,请看效果展示:

图12 - 案例展示

读者在每次绘制完成当前状态下的图像之后,需要点击「清除画布」按钮清除当前画布。

实际上,我们还可以将video元素作为drawImage()方法的图像来源,请看代码示例:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>canvas示例</title>
</head>
<body>
<video src="Lenet.mp4" id="video" controls width="360" height="240" preload="auto"></video>
<button id="btn">绘制图像</button>
<button id="clear">清除画布</button>
<canvas width="500" height="500" style="border: 1px solid red" id="canvas"></canvas>
<script>var canvas = document.getElementById("canvas")var videoEle = document.getElementById("video")var oclear = document.getElementById("clear")var obtn = document.getElementById("btn")if (canvas.getContext){var ctx = canvas.getContext("2d");obtn.onclick = () => {ctx.drawImage(videoEle,0,0,360,360)}oclear.onclick = () => {ctx.clearRect(0,0,500,500)}}
</script>
</body>
</html>

上述代码中,我们可以通过点击「绘制图像」按钮将当前帧图像绘制在canvas中;我们还可以在视频中绘制一个矩形,请看代码示例:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>canvas示例</title>
</head>
<body>
<video src="Lenet.mp4" id="video" controls width="360" height="240" preload="auto"></video>
<button id="btn">开始预测</button>
<button id="clear">清除画布</button>
<canvas width="500" height="500" style="border: 1px solid red" id="canvas"></canvas>
<script>var canvas = document.getElementById("canvas")var videoEle = document.getElementById("video")var oclear = document.getElementById("clear")var obtn = document.getElementById("btn")if (canvas.getContext){var ctx = canvas.getContext("2d");var width = 180;var height = 150;ctx.lineWidth = 5;ctx.strokeStyle = "aqua"obtn.onclick = () => {ctx.drawImage(videoEle,0,0,360,360)ctx.strokeRect(30,90,width,height)ctx.font = "20px bold Arial"ctx.fillStyle ="blue"ctx.fillText("computer",100,85)}oclear.onclick = () => {ctx.clearRect(0,0,500,500)}}
</script>
</body>
</html>

上述代码中,我们在视频中绘制了一个矩形,并用矩形框框出了视频在0:01秒时电脑出现的位置,请看效果演示:

图13 - 案例展示

4.你画我猜(MNIST手写数字版)

讲解完成了<canvas>的基础知识之后,我们来看看本书中用到的<canvas>的相关内容,在你画我猜(MNIST手写数字版)这个案例中,我们先在页面中定义一个<canvas>标签,当用户按下鼠标并拖动时,可以在画布上画出拖动的轨迹,请看代码示例:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>你画我猜(MNIST手写数字版)canvas示例</title>
</head>
<body>
<canvas width="500" height="500" style="border: 1px solid red" id="canvas"></canvas>
<button id="clear">清除画布</button>
<script>var canvas = document.getElementById("canvas")var oclear = document.getElementById("clear")if (canvas.getContext){var ctx = canvas.getContext("2d");canvas.onmousemove = (e) => {if(e.buttons == 1){ctx.fillStyle = "black"ctx.fillRect(e.offsetX,e.offsetY,5,5)}}oclear.onclick = () => {ctx.clearRect(0,0,500,500)}}
</script>
</body>
</html>

请看效果演示:

图13 - 案例展示

5.绘制关键点(人体姿态检测、目标识别)

在本书关于目标检测的案例中,我们需要使用手机的摄像头,并实时的框选出摄像头所采集的每一帧数据中的物体,如图3所示,其中涉及到的canvas相关的知识请参考3.5小节,这里不在赘述。

6.文章最后

以上就是本文的所有内容,小伙伴们学会了嘛?快去实践一下吧!更多详情请关注我的更多开源作品:

1. 微信公众号(hahaCoder)

图14 - 微信公众号

2. 微信小程序(hahaAI)

图15 - 微信小程序

3. Github
链接地址:https://github.com/TURBO1002

Canvas 2D详解相关推荐

  1. 《HTML5 Canvas开发详解》——1.7 2D上下文及其当前状态

    本节书摘来自异步社区<HTML5 Canvas开发详解>一书中的第1章,第1.7节,作者: [美]Steve Fulton , Jeff Fulton 更多章节内容可以访问云栖社区&quo ...

  2. 《HTML5 canvas开发详解(第2版)》——1.9 HTML5 Canvas对象

    本节书摘来自异步社区<HTML5 canvas开发详解(第2版)>一书中的第1章,第1.9节,作者: [美]Steve Fulton , Jeff Fulton 更多章节内容可以访问云栖社 ...

  3. 微信小程序详解 php,微信小程序canvas基础详解

    canvas 元素用于在网页上绘制图形.HTML5 的 canvas 元素使用 JavaScript 在网页上绘制2D图像.本文主要和大家分享微信小程序canvas基础详解,希望能帮助到大家. 一.了 ...

  4. 《Web前端开发精品课——HTML5 Canvas开发详解》——第一部分第二章节

    本节书摘来自异步社区<Web前端开发精品课--HTML5 Canvas开发详解>一书中的第1部分,第2章,作者:莫振杰 著,更多章节内容可以访问云栖社区"异步社区"公众 ...

  5. 《HTML5 canvas开发详解(第2版)》——1.3 本书使用的基础HTML页面

    本节书摘来自异步社区<HTML5 canvas开发详解(第2版)>一书中的第1章,第1.3节,作者: [美]Steve Fulton , Jeff Fulton 更多章节内容可以访问云栖社 ...

  6. Android复习14【高级编程:推荐网址、抠图片上的某一角下来、Bitmap引起的OOM问题、三个绘图工具类详解、画线条、Canvas API详解(平移、旋转、缩放、倾斜)、矩阵详解】

    目   录 推荐网址 抠图片上的某一角下来 8.2.2 Bitmap引起的OOM问题 8.3.1 三个绘图工具类详解 画线条 8.3.16 Canvas API详解(Part 1) 1.transla ...

  7. Android基础入门教程——8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash

    Android基础入门教程--8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash 标签(空格分隔): Android基础入门教程 本节引言: 在Canva ...

  8. Canvas DrawText详解

    声明:内容转载自HenCoder(www.hencoder.com) 感谢大神扔物线 简介 上期的 Paint 详解里已经说过,文字的绘制所能控制的内容太多太细,必须拆成单独的一期专门来讲.今天这期, ...

  9. 《HTML5 Canvas开发详解》——导读

    https://yqfile.alicdn.com/bc81e95aba389209ecb7176965c56b0ba2d9c780.png" > 前言 HTML5 Canvas为开发 ...

最新文章

  1. flannel原理初探针对0.1.0版本
  2. 基于Confluent.Kafka实现的Kafka客户端操作类使用详解
  3. 网卡驱动:stmmac DMA接收流程
  4. Python可以减少代码量?我不信
  5. Linux异步管道多线程速率测试-原始数据
  6. c语言数字分解一个数,关于一道分解整数为N个连数整数的编程题
  7. android 4.3以上修改DNS流程(netd)
  8. Android中为APP创建快捷方式的原理(自己的理解)
  9. wordpress企业网站模板
  10. HTML 标签中的连续的英文折断
  11. Apple苹果终端如何进行批量管理?
  12. 机械键盘各类轴的区别
  13. 大学我这样过,成了别人眼中的大神
  14. java 相关论坛或网站
  15. 包含头文件使用 书名号与双引号的区别(直接打符号竟然发表不了,太难了)
  16. 第一单元 用python学习微积分(七)第一单元总结
  17. 阿里P9整理分享的亿级流量Java高并发与网络编程实战PDF
  18. C语言程序设计谭浩强第五版复习梳理2
  19. 华为解锁账号工具_Redmi 10X 5G版手机怎么一键快速解锁BootLoader-解锁BL教程
  20. 计算机的主要性能指标(计算机组成原理)

热门文章

  1. 通信原理之模拟幅度调制(线性调制)详解
  2. matlab电子双缝衍射,Matlab在《原子物理学》教学中的应用——以电子双缝衍射实验为例...
  3. Swift之语法1(精简版)
  4. 西柏办公用品管理系统(含源代码)
  5. 使用myBatis访问数据库原理
  6. RFID军队资产管理可视化平台-部队资产管理系统
  7. SecureCRT 8.1.4 破解教程
  8. Python3.X BeautifulSoup([your markup], “lxml“) markup_type=markup_type))的解决方案
  9. c语言成绩管理系统总结分析报告,c语言下学生成绩管理系统程序设计报告
  10. python实现验证码识别_python实现图文验证码识别