Canvas 2D详解
大家好,我是石璞东。
在我书的第六章中有一个关于MNIST
手写数字的例子,当数据集加载完成之后,用户可以在<canvas/>
上输入手写数字,点击「预测」按钮之后,浏览器会弹出经模型预测之后的结果;在我书的第九章和第十章中,分别有关于目标检测和人体姿态检测的案例,当关键点的得分符合一定要求时,会通过<canvas/>
将关键部分绘制出来,请看效果:
图1 - MNIST手写数字案例
图2 - 人体姿态检测案例
图3 - 目标检测案例
接下来,我们将在本文中详细讲解一下上述三个模块均用到的一个技术<canvas/>
。
参考资料
- MDN Web Docs:https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API
- 《JavaScript高级程序设计(第三版)》
- 《HTML5权威指南》
1.canvas概述
HTML5最受欢迎的功能就是<canvas>
元素,Canvas API
提供了一个通过JavaScript
和HTML
的<canvas>
元素来绘制图形的方式,可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面,该API
主要聚焦于2D
图形,我们也可以使用WebGL API
绘制硬件加速的2D
和3D
图形。
2.基本用法
使用<canvas>
大概可以分为两个步骤:
- 在
HTML
中定义<canvas>
标签; - 获取
<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>
该标签有两个属性——width
和height
,当没有设置width
和height
的值时,<canvas>
标签会初始化大小为300*150px
的矩形,我们也可以通过CSS
去设置<canvas>
元素的大小,但在绘制时图像会伸缩以适应它的尺寸框架,可能会出现扭曲等情况,所以推荐大家使用width
和height
属性明确的规定宽度和高度,接下来,请看效果展示:
图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>
中绘制了一个左上角(x
,y
)坐标为(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)
该方法用于清除指定矩形区域,让清除部分完全透明;
上述提供的三个方法中均包含相同的参数,x
与y
指定了在<canvas>
画布上所绘制矩形左上角(相对于原点)的坐标,width
和height
设置矩形的尺寸,请看演示案例:
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)
,宽高为width
和height
的矩形;
创建了路径之后,我们可以调用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
表示绘制的最大宽度,为可选参数;
我们可以通过设置font
、textAlign
、textBaseline
和direction
的值来对文本进行渲染,请看详细介绍:
font:表示文本样式、大小及字体,默认字体是
10px sans-serif
;textAlign:表示文本对齐方式,其值包括
left
(文本左对齐)、right
(文本右对齐)、center
(文本居中对齐)、start
(文本对齐界线开始的地方,即左对齐指本地从左向右,右对齐指本地从右向左)、end
(文本对齐界线结束的地方,即左对齐指本地从左向右,右对齐指本地从右向左),默认值为start
;textBaseline:表示文本的基线,其值包括
top
(文本基线在文本块的顶部)、hanging
(文本基线是悬挂基线)、middle
(文本基线在文本块的中间)、alphabetic
(文本基线是标准的字母基线)、ideographic
、bottom
,默认值是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)
其中,image
是image
或者canvas
对象,x
和y
是其在目标canvas
里的起始坐标;
2.drawImage(image, x, y, width, height)
该方法多了两个参数:width
和height
,这两个参数用来控制当向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详解相关推荐
- 《HTML5 Canvas开发详解》——1.7 2D上下文及其当前状态
本节书摘来自异步社区<HTML5 Canvas开发详解>一书中的第1章,第1.7节,作者: [美]Steve Fulton , Jeff Fulton 更多章节内容可以访问云栖社区&quo ...
- 《HTML5 canvas开发详解(第2版)》——1.9 HTML5 Canvas对象
本节书摘来自异步社区<HTML5 canvas开发详解(第2版)>一书中的第1章,第1.9节,作者: [美]Steve Fulton , Jeff Fulton 更多章节内容可以访问云栖社 ...
- 微信小程序详解 php,微信小程序canvas基础详解
canvas 元素用于在网页上绘制图形.HTML5 的 canvas 元素使用 JavaScript 在网页上绘制2D图像.本文主要和大家分享微信小程序canvas基础详解,希望能帮助到大家. 一.了 ...
- 《Web前端开发精品课——HTML5 Canvas开发详解》——第一部分第二章节
本节书摘来自异步社区<Web前端开发精品课--HTML5 Canvas开发详解>一书中的第1部分,第2章,作者:莫振杰 著,更多章节内容可以访问云栖社区"异步社区"公众 ...
- 《HTML5 canvas开发详解(第2版)》——1.3 本书使用的基础HTML页面
本节书摘来自异步社区<HTML5 canvas开发详解(第2版)>一书中的第1章,第1.3节,作者: [美]Steve Fulton , Jeff Fulton 更多章节内容可以访问云栖社 ...
- Android复习14【高级编程:推荐网址、抠图片上的某一角下来、Bitmap引起的OOM问题、三个绘图工具类详解、画线条、Canvas API详解(平移、旋转、缩放、倾斜)、矩阵详解】
目 录 推荐网址 抠图片上的某一角下来 8.2.2 Bitmap引起的OOM问题 8.3.1 三个绘图工具类详解 画线条 8.3.16 Canvas API详解(Part 1) 1.transla ...
- Android基础入门教程——8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash
Android基础入门教程--8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash 标签(空格分隔): Android基础入门教程 本节引言: 在Canva ...
- Canvas DrawText详解
声明:内容转载自HenCoder(www.hencoder.com) 感谢大神扔物线 简介 上期的 Paint 详解里已经说过,文字的绘制所能控制的内容太多太细,必须拆成单独的一期专门来讲.今天这期, ...
- 《HTML5 Canvas开发详解》——导读
https://yqfile.alicdn.com/bc81e95aba389209ecb7176965c56b0ba2d9c780.png" > 前言 HTML5 Canvas为开发 ...
最新文章
- flannel原理初探针对0.1.0版本
- 基于Confluent.Kafka实现的Kafka客户端操作类使用详解
- 网卡驱动:stmmac DMA接收流程
- Python可以减少代码量?我不信
- Linux异步管道多线程速率测试-原始数据
- c语言数字分解一个数,关于一道分解整数为N个连数整数的编程题
- android 4.3以上修改DNS流程(netd)
- Android中为APP创建快捷方式的原理(自己的理解)
- wordpress企业网站模板
- HTML 标签中的连续的英文折断
- Apple苹果终端如何进行批量管理?
- 机械键盘各类轴的区别
- 大学我这样过,成了别人眼中的大神
- java 相关论坛或网站
- 包含头文件使用 书名号与双引号的区别(直接打符号竟然发表不了,太难了)
- 第一单元 用python学习微积分(七)第一单元总结
- 阿里P9整理分享的亿级流量Java高并发与网络编程实战PDF
- C语言程序设计谭浩强第五版复习梳理2
- 华为解锁账号工具_Redmi 10X 5G版手机怎么一键快速解锁BootLoader-解锁BL教程
- 计算机的主要性能指标(计算机组成原理)
热门文章
- 通信原理之模拟幅度调制(线性调制)详解
- matlab电子双缝衍射,Matlab在《原子物理学》教学中的应用——以电子双缝衍射实验为例...
- Swift之语法1(精简版)
- 西柏办公用品管理系统(含源代码)
- 使用myBatis访问数据库原理
- RFID军队资产管理可视化平台-部队资产管理系统
- SecureCRT 8.1.4 破解教程
- Python3.X BeautifulSoup([your markup], “lxml“) markup_type=markup_type))的解决方案
- c语言成绩管理系统总结分析报告,c语言下学生成绩管理系统程序设计报告
- python实现验证码识别_python实现图文验证码识别