Canvas 2D详解

news2024/11/15 8:36:44

在我书的第六章中有一个关于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 = 10
    ctx.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小节,这里不在赘述。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/505652.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年宜昌市中等职业学校技能大赛 “网络搭建与应用”竞赛题-2

2023年宜昌市中等职业学校技能大赛 “网络搭建与应用”竞赛题 一、竞赛内容分布 “网络搭建及应用”竞赛共分二个部分&#xff0c;其中&#xff1a; 第一部分&#xff1a;企业网络搭建部署项目&#xff0c;占总分的比例为50%&#xff1b; 第二部分&#xff1a;企业网络服…

第十四届蓝桥杯大赛软件赛省赛(Java 大学A组)

蓝桥杯 2023年省赛真题 Java 大学A组 试题 A: 特殊日期  试题 B: 与或异或 把填空挂上跟大伙对对答案&#xff0c;先把C/C B组的做了。 试题 A: 特殊日期 本题总分&#xff1a;5 分 【问题描述】 记一个日期为 y y \small yy yy 年 m m \small mm mm 月 d d \small dd dd 日…

链表的初步认识

什么是链表&#xff1f;链表是一种物理存储结构上非连续存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。 就如现实中的火车或铁链一般&#xff0c;环环相扣。当我们到达一个节点时&#xff0c;就可以通过这个节点找到下一个节点。链表与顺序表一样…

【服务器数据恢复】EXT4文件系统下KVM虚拟机数据恢复案例

服务器数据恢复环境&#xff1a; 服务器采用的Linux操作系统EXT4文件系统&#xff1b; 服务器中有3台KVM虚拟机&#xff1a;一台运行Mysql数据库&#xff0c;一台存放数据库备份&#xff0c;一台存放程序代码文件&#xff1b; 每台虚拟机包含一个qcow2格式的磁盘文件和一个raw格…

联合发版+主题演讲|GBASE南大通用亮相鲲鹏开发者峰会2023

5月6-7日&#xff0c;以“创未来 享非凡”为主题的鲲鹏开发者峰会2023在东莞松山湖举办&#xff0c;旨在打造生态伙伴和开发者学习、共享、交流的平台&#xff0c;帮助开发者深入了解鲲鹏、昇腾全栈技术&#xff0c;加速行业技术、产品和解决方案的创新。行业技术领袖、产业技术…

Apache FtpServer在Windows上使用以及SpringBoot中集成apache ftpserver实现Ftp 服务端搭建

场景 Apache Ftpserver Apache FtpServer是100&#xff05;纯Java FTP服务器。它被设计为基于当前可用的开放协议的完整且 可移植的FTP服务器引擎解决方案。FtpServer可以作为Windows服务或Unix / Linux守护程序独立运行&#xff0c; 也可以嵌入Java应用程序中。我们还提供…

【图】邻接表

目录 无向图的邻接表 链表&#xff08;存相邻顶点下标&#xff09;的类 数组里放的顶点 邻接表&#xff08;操作&#xff09; 构造和析构&#xff08;创建销毁邻接表&#xff09; 插入顶点 插入边 获取下标 插v1、v2之间的边 删除顶点 删除边 输出&#xff1a; 其他…

多种采购方式下,数智化招采系统解决方案(实例)

广发证券成立于1991年&#xff0c;是国内首批综合类证券公司&#xff0c;先后于2010年和2015年在深圳证券交易所及香港联合交易所主板上市。 多年来&#xff0c;广发证券在竞争激烈、复杂多变的行业环境中努力开拓、锐意进取&#xff0c;以卓越的经营业绩、持续完善的全面风险…

Node.js对ES6 及更高版本的支持

目录 1、简介 2、默认情况下什么特性随着 Node.js 一起发布&#xff1f; 3、有哪些特性在开发中&#xff1f; 4、移除这个标记&#xff08;--harmony&#xff09;吗 5、Node.js 对应 V8 引擎 1、简介 Node.js 是针对 V8 引擎构建的。通过与此引擎的最新版本保持同步&…

PMP课堂模拟题目及解析(第5期)

41. 项目的混凝土供应商通知项目经理&#xff0c;材料将比预定时间晚三个星期交付。项目经理更新了进度计划并通知项目团队。在这种情况下&#xff0c;哪种合同类型承担的风 险最小&#xff1f; A. 总价加激励费用合同。 B. 总价加经济价格调整合同。 C. 工料合同。 D. 固…

matlab学习笔记

一、序言 1. 图像的输入输出和显示 fimread("test.png"); frgb2gray(f);%rgb图像转化为灰度图像 imshow(f); imwrite(f,"result.jpg","quality",50);%50代表jpg形式压缩质量0-1002. matlab支持的四种图像类别 灰度级图像(Gray-scale images) …

类和对象【C++】【中篇】

目录 一、类的6个默认成员函数 1、构造函数 2、析构函数 3、拷贝构造函数 4、赋值重载函数 二、赋值运算符重载 一、类的6个默认成员函数 注意&#xff1a;默认成员函数不能在类外面定义成全局函数。因为类里没有的话会自动生成&#xff0c;就会产生冲突。 1、构造函数…

k8s采用ansible安装

一、准备工作 测试服务器 服务器配置进程功能备注192.168.0.189CPU:4核 内存32Gansibleansible一键安装k8s192.168.0.141CPU&#xff1a;12核 内存&#xff1a;10Gdocker&#xff0c;kube-apiserver&#xff0c;etcd&#xff0c;kube-scheduler&#xff0c;kube-controller-m…

产品经理 - 原型图设计软件

原型图设计软件哪个好用&#xff1f;6款好用软件推荐&#xff01; - 知乎 摩客, 墨刀 2014 即时设计是一款支持在线协作的专业级 UI 设计工具&#xff0c;用户数已突破230万&#xff1b; 2021年 5,000万(美元) 国外 axure 老牌 如果有进一步模拟的必要&#xff0c;再换用Ad…

JetPack之lifecycle原理分析

Lifecycle是什么 Lifecycle可以有效的避免内存泄漏和解决android生命周期的常见难题Livecycle 是一个表示android生命周期及状态的对象LivecycleOwner 用于连接有生命周期的对象&#xff0c;如activity,fragmentLivecycleObserver 用于观察查LifecycleOwnerLifecycle框架使用观…

关于SSL证书有效期缩短,看这一篇就够了

在TLS/SSL证书有效期不断被缩短的大趋势下&#xff0c;我们在前文和大家聊了聊企业面临的困境、应对策略及证书自动化管理&#xff0c;今天想和大家继续探讨的是TLS/SSL证书自动化管理切实可行的解决方案。 01 自动化证书管理的重要性 TLS/SSL证书是保障网络连接安全的重要手…

图片位深度以及转换办法

位图文件的格式&#xff1a; ① 位图文件头&#xff0c;所用结构体&#xff1a;BITMAPFILEHEADER&#xff0c;占14个字节 ② 位图信息头&#xff0c;所用结构体&#xff1a;BITMAPINFOHEADER&#xff0c;占40个字节 ③ 颜色表项&#xff0c;所用结构体&#xff1a;RGBQUAD&…

Qt5.14版本通用环境配置安装——最详细教学(看不懂,你怪我)

✨✨✨大家好&#xff0c;我是会飞的鱼-blog&#xff0c;今天我来给大家介绍一下Qt5.14.1安装&#xff0c;有不足之处&#xff0c;请大家多多指教。感谢大家支持&#xff01;&#xff01;&#xff01; 目录 前言 安装文件下载&#xff1a; 安装&#xff1a; 设置 QtCreator…

测试相关记录

1、提测计划、需求拆分、颗粒度 2、线上bug回顾 3、提测流水线 4、测试业绩合同指定

6.2.4图的基本操作 6.3.1图的广度优先遍历 6.3.2图的深度优先遍历

6.2.4图的基本操作 考研里面只考邻接矩阵和邻接表的存储结构 思想较为简单见video 重点理解时间复杂度的遍历原理 6.3.1图的广度优先遍历&#xff08;BFS&#xff09;&#xff08;Breadth first traversal&#xff09; 我们从树的广度优先遍历入手去看图的广度优先遍历的思想 …