Canvas:开启web上的图形编程之门

一.概述

        <canvas>元素是HTML5引入的一个新标签,它允许浏览器绘制二维图形和图像。这个元素本身并不具备绘图能力。实际上,它提供的只是一个容器,而绘图操作则需要使用JavaScript来完成。Canvas的API极其丰富,支持绘制文本、线条、路径、矩形、圆形、贝塞尔曲线,以及图像操作等功能。

二.图形编程

1.w3c坐标系


        在web网页,一般提到坐标系大家就能想到下图,没错,Canvas画布所遵循的坐标系统也是这个

2.对应接口 


        HTMLCanvasElement接口定义了相应API的一部分,正是因为它的存在,让我们可以用相应方法操作canvas元素的属性等,下面的图片出自官网HTML Standard,目前还没有人翻译这一部分,所以读起来还是需要结合网上的许多其它资料才能完全理解

3. 基础使用

        先要像如下开启一个canvas画布(默认画布的宽度为300px,高度为150px),再通过js找到相应的canvas元素,然后根据getContext方法

 找到canvas元素的上下文,这个方法根据官网来看可以传入'2D','webgl','webgl2','placeholder',

'bitmaprenderer'或者是'none'这几个参数,入门的话当然是从操作二维元素开始。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style></style>
</head>

<body>
    <canvas id="can" width="300" height="300" style="border: 1px solid purple;"></canvas>
    <script>
        const can = document.getElementById('can')
        //alpha: true 表示Canvas绘图表面包含alpha(透明度)通道,允许绘制半透明的图形。
        //desynchronized: false 旨在减少绘图延迟,对于需要高性能绘制的应用特别有用。
        const context = can.getContext('2d',{alpha: true, desynchronized: false})
    </script>
</body>

</html>

更进阶的使用请看下图(自官网截图)

​ 

4. 直线


  1. moveTo(x1, y1):起点坐标 (x, y)
  2. lineTo(x2, y2):下一个点的坐标 (x, y)
  3. stroke():将所有坐标用一条线连起来

 (1)一条直线

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Document</title>
    <style>
        #can {
            margin: 0 auto;
            margin-top: 200px;
            display: block;
            border: 1px solid hotpink;
        }
    </style>
</head>

<body>
    <canvas id="can" width="300" height="300"></canvas>
    <script>
        const can = document.getElementById('can')
        const context = can.getContext('2d')
        context.moveTo(100, 100)
        context.lineTo(200, 100)
        context.stroke()
    </script>
</body>

</html>

(2)多条直线 

a.相同样式
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Document</title>
    <style>
        #can {
            margin: 0 auto;
            margin-top: 200px;
            display: block;
            border: 1px solid hotpink;
        }
    </style>
</head>

<body>
    <canvas id="can" width="300" height="300"></canvas>
    <script>
        const can = document.getElementById('can')
        const context = can.getContext('2d')
        context.moveTo(20, 100)
        context.lineTo(200, 100)
        context.stroke()

        context.moveTo(20, 150.5)
        context.lineTo(200, 150.5)
        context.stroke()

    </script>
</body>

上面的线条看起来比下面的粗,原因是因为线的中心会和画布像素点的底部对齐, 当像素为整数时,相当于占据了两行像素,一半在上,一半在下。

b.不同样式

也就是说要通过beginPath新开一条路径,分别配上不一样的样式

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Document</title>
    <style>
        #can {
            margin: 0 auto;
            margin-top: 200px;
            display: block;
            border: 1px solid hotpink;
        }
    </style>
</head>

<body>
    <canvas id="can" width="300" height="300"></canvas>
    <script>
        const can = document.getElementById('can')
        const context = can.getContext('2d')
        context.lineWidth = 20
        context.strokeStyle = 'red'
        context.moveTo(20, 100)
        context.lineTo(200, 100)
        context.stroke()

        context.beginPath()
        context.lineWidth = 10
        context.strokeStyle = 'aqua'
        context.moveTo(20, 120.5)
        context.lineTo(200, 120.5)
        context.stroke()

    </script>
</body>

关于线的样式:

 修改线宽:调整lineWidth 

 修改线帽:context.lineCap = "butt"(默认)/"round"/"square"

 修改线条连接方式:context.lineJoin = "miter"(斜接)(默认)/"bevel"(斜面)/"round"(圆角)

 设置斜接长度限制:context.miterLimit = ??

 设置为虚线:context.setLineDash([?,?,?]) //单参的时候实虚线都为该参数的值;双参时实线长度为前值,虚线长度为后值;三参的时候进行轮换。

 获取当前虚线样式:context.getLineDash()

 设置虚线偏移量:context.lineDashOffset = ?

5.文本(Text) 


a.示例

<!DOCTYPE html>
<html>

<head>
    <title>Canvas Text Style 示例</title>
    <style>
        #myCanvas {
            display: block;
            margin: 0 auto;
            border: 1px solid #000;
        }
    </style>
</head>

<body>
    <canvas id="myCanvas" width="600" height="400"></canvas>
    <script>
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
        // 设置文本样式
        ctx.font = '20px Arial'; // 字体大小和字体类型
        ctx.textAlign = 'center'; // 文本对齐方式
        ctx.textBaseline = 'middle'; // 文本基线
        ctx.direction = 'ltr'; // 文本方向

        // 设置填充和描边样式
        ctx.fillStyle = 'blue'; // 填充颜色
        ctx.strokeStyle = 'red'; // 描边颜色

        // 绘制填充文本
        ctx.fillText('Hello Canvas', canvas.width / 2, canvas.height / 2 - 30);

        // 绘制描边文本
        ctx.strokeText('Hello Canvas', canvas.width / 2, canvas.height / 2 + 30);
    </script>
</body>

</html>

b.重点样式详解 

(1)textAlign

文本水平对齐的方式,中间这条线是基准线

<!DOCTYPE html>
<html>

<head>
    <title>Canvas Text Style 示例</title>
    <style>
        #myCanvas {
            display: block;
            margin: 0 auto;
            border: 1px solid #000;
            margin-top: 200px;
        }
    </style>
</head>

<body>
    <canvas id="myCanvas" width="600" height="400"></canvas>

    <script>
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
        ctx.moveTo(300, 0)
        ctx.lineTo(300, 400)
        ctx.strokeStyle = 'red'
        ctx.stroke()

        ctx.font = '30px Arial'

        // 横坐标开始位对齐
        ctx.textAlign = 'start' // 默认值,
        ctx.fillText('得意 start', 300, 40)

        // 横坐标结束位对齐
        ctx.textAlign = 'end' // 结束对齐
        ctx.fillText('得意 end', 300, 100)

        // 左对齐
        ctx.textAlign = 'left' // 左对齐
        ctx.fillText('得意 left', 300, 160)

        // 右对齐
        ctx.textAlign = 'right' // 右对齐
        ctx.fillText('得意 right', 300, 220)

        // 居中对齐
        ctx.textAlign = 'center' // 右对齐
        ctx.fillText('得意 center', 300, 280)
    </script>
</body>

</html>
(2)textBaseline 

 文本垂直对齐方式

 

<!DOCTYPE html>
<html>

<head>
    <title>Canvas Text Style 示例</title>
    <style>
        #myCanvas {
            display: block;
            margin: 0 auto;
            border: 1px solid #000;
            margin-top: 200px;
        }
    </style>
</head>

<body>
    <canvas id="myCanvas" width="700" height="400"></canvas>

    <script>
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
        ctx.moveTo(0, 200)
        ctx.lineTo(700, 200)
        ctx.strokeStyle = 'red'
        ctx.stroke()

        ctx.font = '15px Arial'

        ctx.textBaseline = 'alphabetic'
        ctx.fillText('吉运 alphabetic', 10, 200)

        ctx.textBaseline = 'top'
        ctx.fillText('吉运 top', 130, 200)

        ctx.textBaseline = 'bottom'
        ctx.fillText('吉运 bottom', 200, 200)

        ctx.textBaseline = 'middle'
        ctx.fillText('吉运 middle', 300, 200)

        ctx.textBaseline = 'hanging'
        ctx.fillText('吉运 hanging', 400, 200)

        ctx.textBaseline = 'ideographic'
        ctx.fillText('吉运 ideographic', 500, 200)

    </script>
</body>

</html>
(3)direction

确定文字方向 

<!DOCTYPE html>
<html>

<head>
    <title>Canvas Text Style 示例</title>
    <style>
        #myCanvas {
            display: block;
            margin: 0 auto;
            border: 1px solid #000;
            margin-top: 200px;
        }
    </style>
</head>

<body>
    <canvas id="myCanvas" width="400" height="200"></canvas>

    <script>
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');

        ctx.moveTo(200, 0)
        ctx.lineTo(200, 200)
        ctx.strokeStyle = 'hotpink'
        ctx.stroke()
        ctx.font = '20px Arial';
        ctx.fillText('Hello Canvas (ltr)', 20, 50);

        ctx.direction = 'rtl'; // 设置文本方向为从右到左
        ctx.fillText('Hello Canvas (rtl)', 380, 50);
    </script>
</body>

</html>

6.绘制图形

1. 绘制圆角矩形 (roundRect)

HTML5 Canvas API 原生并不包含roundRect方法,但我们可以通过扩展CanvasRenderingContext2D来添加这个方法。

if (!CanvasRenderingContext2D.prototype.roundRect) {
    CanvasRenderingContext2D.prototype.roundRect = function(x, y, width, height, radius) {
        if (width < 2 * radius) radius = width / 2;
        if (height < 2 * radius) radius = height / 2;
        this.beginPath();
        this.moveTo(x + radius, y);
        this.arcTo(x + width, y, x + width, y + height, radius);
        this.arcTo(x + width, y + height, x, y + height, radius);
        this.arcTo(x, y + height, x, y, radius);
        this.arcTo(x, y, x + width, y, radius);
        this.closePath();
        return this;
    }
}

// 使用roundRect
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');

ctx.roundRect(10, 10, 100, 100, 20);
ctx.fill();

 

2. 绘制矩形 (rect

var ctx = document.getElementById('myCanvas').getContext('2d');

ctx.beginPath();
ctx.rect(20, 20, 150, 100);// rect(x, y, width, height)
ctx.fillStyle = 'green';
ctx.fill();

3. 绘制椭圆 (ellipse)

ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, [anticlockwise]);

  • x:椭圆中心的x坐标。
  • y:椭圆中心的y坐标。
  • radiusX:椭圆的水平半径。
  • radiusY:椭圆的垂直半径。
  • rotation:椭圆旋转的角度,以弧度表示。这个参数定义了椭圆的主轴相对于正x轴的顺时针旋转角度。
  • startAngle:绘制椭圆的起始角,以弧度表示(在椭圆的圆周上,从正x轴顺时针测量)。
  • endAngle:绘制椭圆的结束角,以弧度表示。endAngle的值应该大于startAngle的值,以定义椭圆的绘制范围。
  • anticlockwise(可选):布尔值,表示绘制方向。如果为true,则在两个角度之间逆时针绘制椭圆。如果省略或为false,则顺时针绘制。

 

var ctx = document.getElementById('myCanvas').getContext('2d');

ctx.beginPath();
ctx.ellipse(100, 100, 50, 75, Math.PI / 4, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();

4. 绘制圆弧 (arc)

ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

  • x:圆弧中心的x坐标。
  • y:圆弧中心的y坐标。
  • radius:圆弧的半径。
  • startAngle:绘制圆弧的起始角度,以弧度表示。角度的计算是从正x轴(Canvas的水平轴)开始,顺时针方向。
  • endAngle:绘制圆弧的结束角度,以弧度表示。就像startAngle,这个值也是以弧度计算,从正x轴开始。
  • anticlockwise(可选):布尔值,表示绘制方向。如果为true,则逆时针绘制圆弧。如果省略或为false,则顺时针绘制圆弧。

 

var ctx = document.getElementById('myCanvas').getContext('2d');

ctx.beginPath();
ctx.arc(100, 75, 50, 0, Math.PI * 2); // 绘制一个圆
ctx.fillStyle = 'yellow';
ctx.fill();

5. 使用arcTo绘制圆角路径

ctx.arcTo(x1, y1, x2, y2, radius);

  • x1, y1:第一个控制点的坐标,圆弧起点的方向由当前路径的最后点到此点的直线定义。
  • x2, y2:第二个控制点的坐标,圆弧的终点的方向由此点的坐标定义。实际的圆弧会在x1, y1x2, y2定义的方向之间绘制。
  • radius:圆弧的半径。如果半径设置得过大以至于无法根据控制点和半径绘制圆弧,那么会导致错误,或者浏览器会自动调整半径大小。

 

var ctx = document.getElementById('myCanvas').getContext('2d');

ctx.beginPath();
ctx.moveTo(20, 20);
ctx.arcTo(100, 20, 100, 200, 20);
ctx.arcTo(100, 200, 20, 200, 20);
ctx.lineTo(20, 200);
ctx.strokeStyle = 'purple';
ctx.stroke();

 6. 绘制贝塞尔曲线 (bezierCurveTo)

ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);

  • cp1x, cp1y:第一个控制点的坐标,这个点影响曲线起始部分的弯曲方向和程度。
  • cp2x, cp2y:第二个控制点的坐标,这个点影响曲线末端部分的弯曲方向和程度。
  • x, y:曲线的终点坐标。
var ctx = document.getElementById('myCanvas').getContext('2d');

ctx.beginPath();
ctx.moveTo(20, 20);
ctx.bezierCurveTo(20, 100, 200, 100, 200, 20);
ctx.strokeStyle = 'pink';
ctx.stroke();

7. 绘制二次贝塞尔曲线 (quadraticCurveTo)

ctx.beginPath();
ctx.moveTo(20, 20);
ctx.quadraticCurveTo(20, 100, 200, 20);
ctx.strokeStyle = 'orange';
ctx.stroke();

7.图片


        渲染图片的方式有两种,一种是在JS里加载图片再渲染,另一种是把DOM里的图片拿到 canvas 里渲染 。这里我将采用第二种方式

<!DOCTYPE html>
<html>

<head>
    <title>Canvas示例</title>
    <style>
        #myCanvas {
            display: block;
            margin: 0 auto;
            border: 1px solid #000;
            margin-top: 200px;
        }
        #img {
            display: none;
        }
    </style>
</head>

<body>
    <img src="../duola2.jpg" id="img">
    <canvas id="myCanvas" width="400" height="400"></canvas>
    <script>
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
        const image = document.getElementById('img')
        ctx.drawImage(image, 30, 30) //从(30px,30px)开始渲染,渲染到画布结束
    </script>
</body>

</html>

如果要设置图片宽高,则采用

drawImage(image, dx, dy, dw, dh) 

 之前代码的 ctx.drawImage(image, 30, 30)修改成ctx.drawImage(image, 30, 30,200,200),图像展示为下方的样子

 

如果要截取图片,并定义截取部分的尺寸,则采用

drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)

 将对应代码改成:

ctx.drawImage(image, 100, 100, 100, 100, 20, 20, 100, 100)

 

 三.总结

        官网的解释更加详细,这边只是语义化更强一点,遇到模棱两可的地方当然还是光顾官网HTML Standard (whatwg.org) ,当然canvas的部分当然不止这一点,之后会慢慢补充的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2.5网安学习第二阶段第五周回顾(个人学习记录使用)

本周重点 ①多进程和多线程 1、进程和线程 2、多线程爆破 ②Redis数据库 1、Redis的使用 2、Redis持久化 3、Redis未授权免密登录 ③嗅探和Python攻击脚本 1、嗅探&#xff08;端口扫描和IP扫描&#xff09; 2、SCAPY的应用 3、Python攻击脚本&#xff08;SYN半连接…

Jmeter教程-JMeter 环境安装及配置

Jmeter教程 JMeter 环境安装及配置 在使用 JMeter 之前&#xff0c;需要配置相应的环境&#xff0c;包括安装 JDK 和获取 JMeter ZIP 包。 安装JDK 1.JDK下载 示例环境为Windows11环境&#xff0c;读者应根据实际环境下载JDK的安装包。 JDK下载地址&#xff1a; Java21 下载 …

JavaWeb——004Maven SpringBootWeb入门

一、Maven 1、什么是maven&#xff1f; 2、Maven的作用是什么&#xff1f;&#xff08;3种&#xff09; 1.1、方便的依赖管理 依赖管理&#xff1a;有了Maven&#xff0c;我们就不用再手动导入Jar包了&#xff0c;我们只需要在配置文件当中&#xff0c;简单描述一下项目所需要…

Thymeleaf无法显示模板视图,加载页面显示404状态问题的解决方法

本篇文章主要讲解&#xff1a;Thymeleaf无法显示模板视图&#xff0c;加载页面显示404状态问题的解决方法 日期&#xff1a;2024年2月23日 作者&#xff1a;任聪聪 现象说明&#xff1a; 1.只返回输出模板的名称&#xff0c;如图&#xff1a; 2.显示报错信息&#xff1a; Whi…

【学网攻】 第(30)节 -- 综合实验三

系列文章目录 目录 系列文章目录 文章目录 前言 一、综合实验 二、实验 1.引入 实验目标 实验设备 实验拓扑图 实验配置 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 -- 交换机配置聚合端口【学网攻】 第(4)节…

Python爬虫-报错requests.exceptions.SSLError: HTTPSConnectionPool

在学习python爬虫&#xff0c;在公司运行代码没有问题&#xff0c;但是下班回来把代码拉下来运行&#xff0c;却出现问题。 问题&#xff1a; requests.exceptions.SSLError: HTTPSConnectionPool(host‘campusgateway.51job.com’, port443): Max retries exceeded with url…

Intel PT简介以及perf 使用 Intel pt

文章目录 前言一、工作原理二、追踪执行流程三、追踪定时信息四、perf使用 intel pt4.1 perf record4.2 perf report4.3 perf script 五、与 Intel LBR 比较六、perf 对 Intel pt 的支持参考资料 前言 代码插装是最古老的性能分析方法之一。我们经常使用它。在函数开头插入pri…

多任务爬虫(多线程和多进程)

在一台计算机中&#xff0c;我们可以同时打开多个软件&#xff0c;例如同时浏览网页、听音乐、打字等&#xff0c;这是再正常不过的事情。但仔细想想&#xff0c;为什么计算机可以同时运行这么多软件呢? 这就涉及计算机中的两个名词&#xff1a;多进程和多线程。 同样&#xf…

QT_day4

1.思维导图 2. 输入闹钟时间格式是小时:分钟 widget.cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);id startTimer(1000);flag1;speecher new QTextT…

如何搭建Facebook直播网络?

在当今数字化时代&#xff0c;Facebook直播已经成为了一种极具吸引力的社交形式&#xff0c;为个人和企业提供了与观众直接互动的机会&#xff0c;成为推广产品、分享经验、建立品牌形象的重要途径。然而&#xff0c;对于许多人来说&#xff0c;搭建一个稳定、高质量的 Faceboo…

前端(vue)数据存储方案

引言 本需求文档旨在明确前端项目中的数据存储需求&#xff0c;包括数据类型、数据结构、数据交互方式等。它定义了前端项目中需要存储和处理的数据&#xff0c;以及对这些数据进行访问和操作的要求。 功能需求 数据存储按数据类型分为 持久存储、内存存储&#xff08;响应式…

【网络安全 | 网络协议】一文讲清HTTP协议

HTTP概念简述 HTTP&#xff08;Hypertext Transfer Protocol&#xff09;协议&#xff0c;又称超文本传输协议&#xff0c;用于传输文本、图像、音频、视频以及其他多媒体文件。它是Web应用程序通信的基础&#xff0c;通过HTTP协议&#xff0c;Web浏览器可以向Web服务器发起请…

RabbitMQ监控方法以及核心指标

RabbitMQ监控方法以及核心指标 1. 监控指标采集2. 使用rabbimq插件采集指标2.1 3.8.0之前版本&#xff0c;使用外部插件暴露2.2 3.8.0之后版本&#xff0c;使用内置插件暴露 3. 使用rabbitmq_exporter采集指标3.1 部署rabbitmq_exporter3.2 prometheus采集rabbitmq_exporter的暴…

二、基本语法

一、变量声明 1、语法 <变量名称>: <变量类型> <变量值> 2、变量类型 字符串&#xff1a;string 数值&#xff0c;整数、浮点数都可以&#xff1a;number 布尔&#xff1a;boolean 任意类型&#xff1a;any 联合类型&#xff0c;指定的多个类型中的…

springmvc+mybatis+springboot航空飞机订票售票系统_f48cp

互联网发展的越来越快了&#xff0c;在当下社会节点&#xff0c;人们也开始越来越依赖互联网。通过互联网信息和数据&#xff0c;极大地满足用户要求[5]。飞机订票系统使用了B/S模式&#xff0c;并且不需要安装第三方插件&#xff0c;他们甚至能直接在电脑上随机随地实现飞机订…

JavaScript运算符

文章目录 运算符介绍算术运算符递增和递减运算符比较运算符逻辑运算符短路运算逻辑与 逻辑或 赋值运算符运算符优先级 运算符介绍 算术运算符 %取余运算符的主要用途&#xff1a; 判断某个数是否能被某个数整除。 浮点数的精度问题&#xff1a; 所以&#xff1a;不要直接判断…

《一本书讲透 Elasticsearch》荣登当当人工智能新书榜

年前&#xff0c;《一本书讲透 Elasticsearch》荣登京东编程语言与程序设计榜前5名&#xff0c;今天又上榜当当人工智能新书榜第7名。 先看评价&#xff0c;看看大家阅后反馈 来自百度公司员工评价 来自Elastic原厂资深架构师评价 来自IBM资深架构师周钰老师的评价 来自2位阿里…

【Java程序员面试专栏 数据结构】一 高频面试算法题:数组

一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是汇总的高频题目,本篇主要聊聊数组,包括数组合并,滑动窗口解决最长无重复子数组问题,图形法解下一个排列问题,以及一些常见的二维矩阵问题,所以放到一篇Blog中集中练习 题目…

鸿蒙开发市场憧憬如何?是否值得一冲~

自从华为放话&#xff1a;鸿蒙NEXT 不再支持安卓操作系统。换句话说&#xff0c;华为就是在向全世界宣布&#xff0c;华为官宣于2024年&#xff0c;不再支持安卓的AOSP。 大家也应该知道&#xff0c;谷歌暂停与华为的合作。为了生存&#xff0c;华为被迫突出了自研的鸿蒙操作系…

物麒平台自定义事件代码修改流程

是否需要申请加入数字音频系统研究开发交流答疑群(课题组)&#xff1f;可加我微信hezkz17, 本群提供音频技术答疑服务&#xff0c;群赠送蓝牙音频&#xff0c;DSP音频项目核心开发资料, 1 配置工具对应关系 2 事件处理 3 事件定义 4