Canvas 前端艺术家

目前各种数据来看,前端未来在 数据可视化AI 这两个领域会比较香,而 Canvas 是 数据可视化 在前端方面的基础技术。所以给大家唠唠Canvas这个魔幻工具。

Canvas 介绍

  • Canvas 中文名叫 “画布”,是 HTML5 新增的一个标签
  • Canvas 允许开发者通过 JS在这个标签上 绘制各种图案
  • Canvas 拥有多种绘制路径、矩形、圆形、字符以及图片的方法。
  • Canvas 在某些情况下可以 “代替” 图片。
  • Canvas 可用于动画、游戏、数据可视化、图片编辑器、实时视频处理等领域。

SVG 的区别

CanvasSVG
用JS动态生成元素(一个HTML元素)用XML描述元素(类似HTML元素那样,可用多个元素来描述一个图形)
位图(受屏幕分辨率影响)矢量图(不受屏幕分辨率影响)
不支持事件支持事件
数据发生变化需要重绘不需要重绘

如果感觉上面的描述难懂,可以打开 AntV 旗下的图形编辑引擎做对比。G6 是使用 canvas 开发的,X6 是使用 svg 开发的。

个人建议是:如果要展示的数据量比较大,比如一条数据就是一个元素节点,那使用 canvas 会比较合适;如果用户操作的交互比较多,而且对清晰度有要求(矢量图),那么使用 svg 会比较合适。

入门

画 直线

基本步骤:

  1. HTML 中创建 canvas 元素
  2. 通过 js 获取 canvas 标签
  3. canvas 标签中获取到绘图工具
  4. 通过绘图工具,在 canvas 标签上绘制图形
<!-- 1、创建 canvas 元素 -->
<canvasid="c"width="300"height="200"style="border: 1px solid #ccc;"
></canvas><script>// 2、获取 canvas 对象const cnv = document.getElementById('c')// 3、获取 canvas 上下文环境对象const cxt = cnv.getContext('2d')// 4、绘制图形cxt.moveTo(100, 100) // 起点坐标 (x, y)cxt.lineTo(200, 100) // 终点坐标 (x, y)cxt.stroke() // 将起点和终点连接起来
</script>

在这里插入图片描述

1、默认宽高

canvas默认的 宽度(300px) 和 高度(150px)

如果不在 canvas 上设置宽高,那 canvas 元素的默认宽度是300px,默认高度是150px。

2、设置 canvas 宽高

canvas 元素提供了 widthheight 两个属性,可设置它的宽高。

需要注意的是,这两个属性只需传入数值,不需要传入单位(比如 px 等)。

<canvas width="600" height="400"></canvas>

3、不能通过 CSS 设置画布的宽高

使用 css 设置 canvas 的宽高,会出现 内容被拉伸 的不良后果!

<style>#c {width: 400px;height: 400px;border: 1px solid #ccc;}
</style><canvas id="c"></canvas><script>// 1、获取canvas对象const cnv = document.getElementById('c')// 2、获取canvas上下文环境对象const cxt = cnv.getContext('2d')// 3、绘制图形cxt.moveTo(100, 100) // 起点cxt.lineTo(200, 100) // 终点cxt.stroke() // 将起点和终点连接起来console.log(cnv.width) // 获取 canvas 的宽度,输出:300console.log(cnv.height) // 获取 canvas 的高度,输出:150
</script>

canvas 的默认宽度是300px,默认高度是150px。

  1. 如果使用 css 修改 canvas 的宽高(比如本例变成 400px * 400px),那宽度就由 300px 拉伸到 400px,高度由 150px 拉伸到 400px。
  2. 使用 js 获取 canvas 的宽高,此时返回的是 canvas 的默认值。

4、线条 默认宽度 和 颜色

线条的默认宽度是 1px ,默认颜色是 黑色

但由于默认情况下 canvas 会将线条的中心点 和 像素的底部 对齐,因此就会导致显示效果是 2px 和非纯黑色问题。

5、IE兼容性高

暂时只有 IE 9 以上才支持 canvas 。但好消息是 IE 已经凉了。

如需兼容 IE 7 和 8 ,可以使用 ExplorerCanvas 。但即使是使用了 ExplorerCanvas 仍然会有所限制,比如无法使用 fillText() 方法等。

基础图形

坐标系

Canvas 使用的是 W3C 坐标系 ,也就是遵循我们屏幕、报纸的 阅读习惯 ,从上往下,从左往右。
W3C 坐标系数学直角坐标系X轴 是一样的,只是 Y轴 的反向相反。
W3C 坐标系Y轴 正方向向下。
在这里插入图片描述

直线

一条直线

最简单的起步方式是画一条直线。这里所说的 “直线” 是几何学里的 “线段” 的意思。

需要用到这3个方法:

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

起步阶段可以先这样理解。
在这里插入图片描述

<canvas id="c" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 绘制直线cxt.moveTo(50, 100) // 起点坐标cxt.lineTo(200, 50) // 下一个点的坐标cxt.stroke() // 将上面的坐标用一条线连接起来
</script>

上面的代码所呈现的效果,可以看下图解释(手不太聪明,画得不是很标准,希望能看懂)
在这里插入图片描述

多条直线

如需画多条直线,可以用会上面那几个方法。

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.moveTo(20, 100)cxt.lineTo(200, 100)cxt.stroke()cxt.moveTo(20, 120.5)cxt.lineTo(200, 120.5)cxt.stroke()
</script>

在这里插入图片描述

可以看到 两条线的 粗细不一样。

这是因为 默认情况下 canvas 会将线条的中心点和像素的底部对齐,所以会导致显示效果是 2px 和非纯黑色问题。

在这里插入图片描述
线的中心点会和画布像素点的底部对齐,所以会线中间是黑色的,但由于一个像素就不能再切割了,所以会有半个像素被染色,就变成了浅灰色。

所以如果你设置的 Y轴 值是一个整数,就会出现上面那种情况。

设置样式

  • lineWidth:线的粗细
  • strokeStyle:线的颜色
  • lineCap:线帽。
    默认: butt; 圆形: round; 方形: square

在这里插入图片描述

<canvas id="c" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 绘制直线cxt.moveTo(50, 50)cxt.lineTo(200, 50)// 修改直线的宽度cxt.lineWidth = 20// 修改直线的颜色cxt.strokeStyle = 'pink'// 修改直线两端样式cxt.lineCap = 'round' // 默认: butt; 圆形: round; 方形: squarecxt.stroke()
</script>

新开路径

开辟新路径的方法:

  • beginPath()

在绘制多条线段的同时,还要设置线段样式,通常需要开辟新路径。

要不然样式之间会相互污染。

比如这样:在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 第一条线cxt.moveTo(20, 100)cxt.lineTo(200, 100)cxt.lineWidth = 10cxt.strokeStyle = 'pink'cxt.stroke()// 第二条线cxt.moveTo(20, 120.5)cxt.lineTo(200, 120.5)cxt.stroke()
</script>

如果不想相互污染,需要做2件事:

  1. 使用 beginPath() 方法,重新开一个路径
  2. 设置新线段的样式(必须项)

如果上面2步却了其中1步都会有影响。

只使用 beginPath()

在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 第一条线cxt.moveTo(20, 100)cxt.lineTo(200, 100)cxt.lineWidth = 10cxt.strokeStyle = 'pink'cxt.stroke()// 第二条线cxt.beginPath() // 重新开启一个路径cxt.moveTo(20, 120.5)cxt.lineTo(200, 120.5)cxt.stroke()
</script>

第一条线的样式会影响之后的线。

但如果使用了 beginPath() ,后面的线段不会影响前面的线段。
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 第一条线cxt.moveTo(20, 100)cxt.lineTo(200, 100)cxt.stroke()// 第二条线cxt.beginPath() // 重新开启一个路径cxt.moveTo(20, 120.5)cxt.lineTo(200, 120.5)cxt.lineWidth = 4cxt.strokeStyle = 'red'cxt.stroke()
</script>
设置新线段的样式,没使用 beginPath() 的情况

这个情况会反过来,后面的线能影响前面的线。
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 第一条线cxt.moveTo(20, 100)cxt.lineTo(200, 100)cxt.lineWidth = 10cxt.strokeStyle = 'pink'cxt.stroke()// 第二条线cxt.moveTo(20, 120.5)cxt.lineTo(200, 120.5)cxt.lineWidth = 4cxt.strokeStyle = 'red'cxt.stroke()
</script>
正确的做法

在设置 beginPath() 的同时,也各自设置样式。这样就能做到相互不影响了。
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.moveTo(20, 100)cxt.lineTo(200, 100)cxt.lineWidth = 10cxt.strokeStyle = 'pink'cxt.stroke()cxt.beginPath() // 重新开启一个路径cxt.moveTo(20, 120.5)cxt.lineTo(200, 120.5)cxt.lineWidth = 4cxt.strokeStyle = 'red'cxt.stroke()
</script>

折线

直线 差不多,都是使用 moveTo()lineTo()stroke() 方法可以绘制折线。
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.moveTo(50, 200)cxt.lineTo(100, 50)cxt.lineTo(200, 200)cxt.lineTo(250, 50)cxt.stroke()
</script>

画这种折线,最好在草稿纸上画一个坐标系,自己计算并描绘一下每个点大概在什么什么位置,最后在 canvas 中看看效果。

矩形

根据前面的基础,我们可以 使用线段来描绘矩形,但 canvas 也提供了 rect() 等方法可以直接生成矩形。

使用线段描绘矩形

可以使用前面画线段的方法来绘制矩形
在这里插入图片描述

canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 绘制矩形cxt.moveTo(50, 50)cxt.lineTo(200, 50)cxt.lineTo(200, 120)cxt.lineTo(50, 120)cxt.lineTo(50, 50) // 需要闭合,又或者使用 closePath() 方法进行闭合,推荐使用 closePath()cxt.stroke()
</script>

使用 strokeRect() 描边矩形

  • strokeStyle:设置描边的属性(颜色、渐变、图案)
  • strokeRect(x, y, width, height):描边矩形(x和y是矩形左上角起点;width 和 height 是矩形的宽高)
  • strokeStyle 必须写在 strokeRect() 前面,不然样式不生效。
    在这里插入图片描述
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// strokeStyle 属性// strokeRect(x, y, width, height) 方法cxt.strokeStyle = 'pink'cxt.strokeRect(50, 50, 200, 100)
</script>

上面的代码可以这样理解
在这里插入图片描述

使用 fillRect() 填充矩形

fillRect()strokeRect() 方法差不多,但 fillRect() 的作用是填充。

需要注意的是,fillStyle 必须写在 fillRect() 之前,不然样式不生效。
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// fillStyle 属性// fillRect(x, y, width, height) 方法cxt.fillStyle = 'pink'cxt.fillRect(50, 50, 200, 100) // fillRect(x, y, width, height)
</script>

同时使用 strokeRect()fillRect()

同时使用 strokeRect()fillRect() 会产生描边和填充的效果
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.strokeStyle = 'red'cxt.strokeRect(50, 50, 200, 100) // strokeRect(x, y, width, height)cxt.fillStyle = 'yellow'cxt.fillRect(50, 50, 200, 100) // fillRect(x, y, width, height)
</script>

使用 rect() 生成矩形

rect()fillRect() 、strokeRect() 的用法差不多,唯一的区别是:

strokeRect()fillRect() 这两个方法调用后会立即绘制;rect() 方法被调用后,不会立刻绘制矩形,而是需要调用 stroke()fill() 辅助渲染。
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.strokeStyle = 'red'cxt.fillStyle = 'pink'cxt.rect(50, 50, 200, 100) // rect(x, y, width, height)cxt.stroke()cxt.fill()
</script>

等价公式:

cxt.strokeStyle = 'red',
cxt.rect(50, 50, 200, 100)
cxt.stroke()// 等价于
cxt.strokeStyle = 'red'
cxt.strokerect(50, 50, 200, 100)// -----------------------------cxt.fillStyle = 'hotpink'
cxt.rect(50, 50, 200, 100)
cxt.fill()// 等价于
cxt.fillStyle = 'yellowgreen'
cxt.fillRect(50, 50, 200, 100)

使用 clearRect() 清空矩形

使用 clearRect() 方法可以清空指定区域。

clearRect(x, y, width, height)

其语法和创建 cxt.rect() 差不多。

在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.fillStyle = 'pink' // 设置填充颜色cxt.fillRect(50, 50, 200, 200) // 填充矩形cxt.clearRect(60, 60, 180, 90) // 清空矩形
</script>

清空画布

canvas 画布元素是矩形,所以可以通过下面的代码把整个画布清空掉。

// 省略部分代码cxt.clearRect(0, 0, cnv.width, cnv.height)

要清空的区域:从画布左上角开始,直到画布的宽和画布的高为止。

多边形

Canvas 要画多边形,需要使用 moveTo()lineTo()closePath()

三角形

虽然三角形是常见图形,但 canvas 并没有提供类似 rect() 的方法来绘制三角形。

需要确定三角形3个点的坐标位置,然后使用 stroke() 或者 fill() 方法生成三角形。

在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.moveTo(50, 50)cxt.lineTo(200, 50)cxt.lineTo(200, 200)// 注意点:如果使用 lineTo 闭合图形,是不能很好闭合拐角位的。cxt.lineTo(50, 50) // 闭合cxt.stroke()</script>

注意,默认情况下不会自动从最后一个点连接到起点。最后一步需要设置一下 cxt.lineTo(50, 50) ,让它与 cxt.moveTo(50, 50) 一样。这样可以让路径回到起点,形成一个闭合效果。

但这样做其实是有点问题的,而且也比较麻烦,要记住起始点坐标。

上面的闭合操作,如果遇到设置了 lineWidth 或者 lineJoin 就会有问题,比如:

// 省略部分代码
cxt.lineWidth = 20

当线段变粗后,起始点和结束点的链接处,拐角就出现“不正常”现象。
在这里插入图片描述

如果需要真正闭合,可以使用 closePath() 方法。
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.moveTo(50, 50)cxt.lineTo(200, 50)cxt.lineTo(200, 200)// 手动闭合cxt.closePath()cxt.lineJoin = 'miter' // 线条连接的样式。miter: 默认; bevel: 斜面; round: 圆角cxt.lineWidth = 20cxt.stroke()
</script>

使用 cxt.closePath() 可以自动将终点和起始点连接起来,此时看上去就正常多了。

菱形

在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.moveTo(150, 50)cxt.lineTo(250, 100)cxt.lineTo(150, 150)cxt.lineTo(50, 100)cxt.closePath()cxt.stroke()
</script>

要绘制 直线类型 的图形,在草稿纸上标记出起始点和每个拐角的点,然后再连线即可。相对曲线图形来说,直线图形是比较容易的。

圆形

绘制圆形的方法是 arc()

语法:

arc(x, y, r, sAngle, eAngle,counterclockwise)
  • xy: 圆心坐标
  • r: 半径
  • sAngle: 开始角度
  • eAngle: 结束角度
  • counterclockwise: 绘制方向(true: 逆时针; false: 顺时针),默认 false
    在这里插入图片描述
    开始角度和结束角度,都是以弧度为单位。例如 180°就写成 Math.PI ,360°写成 Math.PI * 2 ,以此类推。

在实际开发中,为了让自己或者别的开发者更容易看懂弧度的数值,1°应该写成 Math.PI / 180

  • 100°: 100 * Math.PI / 180
  • 110°: 110 * Math.PI / 180
  • 241°: 241 * Math.PI / 180

注意:绘制圆形之前,必须先调用 beginPath() 方法!!!
在绘制完成之后,还需要调用 closePath() 方法!!!

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/440ffb9c8d2e4fc391ebdd0026b86145.png

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.beginPath()cxt.arc(150, 150, 80, 0, 360 * Math.PI / 180)cxt.closePath()cxt.stroke()
</script>

半圆

如果使用 arc() 方法画圆时,没做到刚好绕完一周(360°)就直接闭合路径,就会出现半圆的状态。

30.png

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.beginPath()cxt.arc(150, 150, 100, 0, 180 * Math.PI / 180) // 顺时针cxt.closePath()cxt.stroke()
</script>

上面的代码中,cxt.arc 最后一个参数没传,默认是 false ,所以是顺时针绘制。
在这里插入图片描述

如果希望半圆的弧面在上方,可以将 cxt.arc 最后一个参数设置成 true
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.beginPath()cxt.arc(150, 150, 100, 0, 180 * Math.PI / 180, true)cxt.closePath()cxt.stroke()
</script>

弧线

使用 arc() 方法画半圆时,如果最后不调用 closePath() 方法,就不会出现闭合路径。也就是说,那是一条弧线。

canvas 中,画弧线有2中方法:arc()arcTo()

arc() 画弧线

如果想画一条 0° ~ 30° 的弧线,可以这样写
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.beginPath()cxt.arc(150, 150, 100, 0, 30 * Math.PI / 180)cxt.stroke()
</script>

原理如下图所示,红线代表画出来的那条弧线。
在这里插入图片描述

arcTo() 画弧线

arcTo() 的使用方法会更加复杂,如果初学看不太懂的话可以先跳过,看完后面的再回来补补。

语法:

arcTo(cx, cy, x2, y2, radius)
  • cx: 两切线交点的横坐标
  • cy: 两切线交点的纵坐标
  • x2: 结束点的横坐标
  • y2: 结束点的纵坐标
  • radius: 半径

其中,(cx, cy) 也叫控制点,(x2, y2) 也叫结束点。

是不是有点奇怪,为什么没有 x1y1

(x1, y1) 是开始点,通常是由 moveTo() 或者 lineTo() 提供。

arcTo() 方法利用 开始点、控制点和结束点形成的夹角,绘制一段与夹角的两边相切并且半径为 radius 的圆弧
在这里插入图片描述

举个例子
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.moveTo(40, 40)cxt.arcTo(120, 40, 120, 120, 80)cxt.stroke()
</script>

基础样式

描边 stroke()

前面讲到过,这里不再多说。

线条宽度 lineWidth

lineWidth 默认值是 1 ,默认单位是 px

语法:

lineWidth = 线宽

线条颜色 strokeStyle

使用 strokeStyle 可以设置线条颜色

语法:

strokeStyle = 颜色值

线帽 lineCap

线帽指的是线段的开始和结尾处的样式,使用 lineCap 可以设置

语法:

lineCap = '属性值'

属性值包括:

  • butt: 默认值,无线帽
  • square: 方形线帽
  • round: 圆形线帽
    在这里插入图片描述
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 设置线宽,方便演示cxt.lineWidth = 16// 默认线帽 buttcxt.beginPath()cxt.moveTo(50, 60)cxt.lineTo(250, 60)cxt.stroke()// 方形线帽 squarecxt.beginPath()cxt.lineCap = 'square'cxt.moveTo(50, 150)cxt.lineTo(250, 150)cxt.stroke()// 圆形线帽 roundcxt.beginPath()cxt.lineCap = 'round'cxt.moveTo(50, 250)cxt.lineTo(250, 250)cxt.stroke()
</script>

使用 squareround 的话,会使线条变得稍微长一点点,这是给线条增加线帽的部分,这个长度在日常开发中需要注意。

线帽 只对线条的 开始 和 结尾 处产生作用,对 拐角 不会产生任何作用。

拐角样式 lineJoin

如果需要设置拐角样式,可以使用 lineJoin

语法:

lineJoin = '属性值'

属性值包括:

  • miter: 默认值,尖角
  • round: 圆角
  • bevel: 斜角
    在这里插入图片描述
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.lineWidth = 20// 默认,尖角cxt.moveTo(50, 40)cxt.lineTo(200, 40)cxt.lineTo(200, 90)cxt.stroke()// 斜角 bevelcxt.beginPath()cxt.moveTo(50, 140)cxt.lineTo(200, 140)cxt.lineTo(200, 190)cxt.lineJoin = 'bevel'cxt.stroke()// 圆角 roundcxt.beginPath()cxt.moveTo(50, 240)cxt.lineTo(200, 240)cxt.lineTo(200, 290)cxt.lineJoin = 'round'cxt.stroke()
</script>

虚线 setLineDash()

使用 setLineDash() 方法可以将描边设置成虚线。

语法:

setLineDash([])

需要传入一个数组,且元素是数值型。

虚线分3种情况

  1. 只传1个值
  2. 有2个值
  3. 有3个以上的值

在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.lineWidth = 20cxt.strokeStyle = 'pink'cxt.moveTo(50, 50)cxt.lineTo(200, 50)cxt.setLineDash([10]) // 只传1个参数,实线与空白都是 10pxcxt.stroke()cxt.beginPath()cxt.moveTo(50, 100)cxt.lineTo(200, 100)cxt.setLineDash([10, 20]) // 2个参数,此时,实线是 10px, 空白 20pxcxt.stroke()cxt.beginPath()cxt.moveTo(50, 150)cxt.lineTo(200, 150)cxt.setLineDash([10, 20, 5]) // 传3个以上的参数,此例:10px实线,20px空白,5px实线,10px空白,20px实线,5px空白 ……cxt.stroke()
</script>

此外,还可以始终 cxt.getLineDash() 获取虚线不重复的距离;

cxt.lineDashOffset 设置虚线的偏移位。

填充

使用 fill() 可以填充图形,根据前面的例子应该掌握了如何使用 fill()
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.fillStyle = 'pink'cxt.rect(50, 50, 200, 100)cxt.fill()
</script>

可以使用 fillStyle 设置填充颜色,默认是黑色。

非零环绕填充

在这里插入图片描述
在使用 fill() 方法填充时,需要注意一个规则:非零环绕填充

这样说有点复杂,先看看例子
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 外层矩形cxt.moveTo(50, 50)cxt.lineTo(250, 50)cxt.lineTo(250, 250)cxt.lineTo(50, 250)cxt.closePath()// 内层矩形cxt.moveTo(200, 100)cxt.lineTo(100, 100)cxt.lineTo(100, 200)cxt.lineTo(200, 200)cxt.closePath()cxt.fill()
</script>

请看看上面的代码,我画了2个矩形,它们都没有用 beginPath() 方法开辟新路径。
在这里插入图片描述

内层矩形是逆时针绘制的,所以内层的值是 -1 ,它又经过外层矩形,而外层矩形是顺时针绘制,所以经过外层时值 +1,最终内层的值为 0 ,所以不会被填充。

文本

Canvas 提供了一些操作文本的方法。

为了方便演示,先了解一下在 Canvas 中如何给本文设置样式。

样式 font

CSS 设置 font 差不多,Canvas 也可以通过 font 设置样式。

语法:

cxt.font = 'font-style font-variant font-weight font-size/line-height font-family'

如果需要设置字号 font-size,需要同时设置 font-family

cxt.font = '30px 宋体'

描边 strokeText()

使用 strokeText() 方法进行文本描边

语法:

strokeText(text, x, y, maxWidth)
  • text: 字符串,要绘制的内容
  • x: 横坐标,文本左边要对齐的坐标(默认左对齐)
  • y: 纵坐标,文本底边要对齐的坐标
  • maxWidth: 可选参数,表示文本渲染的最大宽度(px),如果文本超出 maxWidth 设置的值,文本会被压缩。
    在这里插入图片描述
<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.font = '60px Arial' // 将字号设置成 60px,方便观察cxt.strokeText('雷猴', 30, 90)
</script>

设置描边颜色 strokeStyle

使用 strokeStyle 设置描边颜色。
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.font = '60px Arial' // 将字号设置成 60px,方便观察cxt.strokeStyle = 'pink' // 设置文本描边颜色cxt.strokeText('雷猴', 30, 90)
</script>

填充 fillText

使用 fillText() 可填充文本。

语法和 strokeText() 一样。

fillText(text, x, y, maxWidth)

在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.font = '60px Arial'cxt.fillText('雷猴', 30, 90)
</script>

设置填充颜色 fillStyle

使用 fillStyle 可以设置文本填充颜色。
在这里插入图片描述

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')cxt.font = '60px Arial'cxt.fillStyle = 'pink'cxt.fillText('雷猴', 30, 90)
</script>

获取文本长度 measureText()

measureText().width 方法可以获取文本的长度,单位是 px

<canvas id="c" width="300" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')let text = '雷猴'cxt.font = 'bold 40px Arial'cxt.fillText(text, 40, 80)console.log(cxt.measureText(text).width) // 80
</script>

水平对齐方式 textAlign

使用 textAlign 属性可以设置文字的水平对齐方式,一共有5个值可选

  • start: 默认。在指定位置的横坐标开始。
  • end: 在指定坐标的横坐标结束。
  • left: 左对齐。
  • right: 右对齐。
  • center: 居中对齐。

在这里插入图片描述

红线是辅助参考线。

<canvas id="c" width="400" height="400" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 竖向的辅助线(参考线,在画布中间)cxt.moveTo(200, 0)cxt.lineTo(200, 400)cxt.strokeStyle = 'red'cxt.stroke()cxt.font = '30px Arial'// 横坐标开始位对齐cxt.textAlign = 'start' // 默认值,cxt.fillText('雷猴 start', 200, 40)// 横坐标结束位对齐cxt.textAlign = 'end' // 结束对齐cxt.fillText('雷猴 end', 200, 100)// 左对齐cxt.textAlign = 'left' // 左对齐cxt.fillText('雷猴 left', 200, 160)// 右对齐cxt.textAlign = 'right' // 右对齐cxt.fillText('雷猴 right', 200, 220)// 居中对齐cxt.textAlign = 'center' // 右对齐cxt.fillText('雷猴 center', 200, 280)
</script>

从上面的例子看,startleft 的效果好像是一样的,endright 也好像是一样的。

在大多数情况下,它们的确一样。但在某些国家或者某些场合,阅读文字的习惯是 从右往左 时,start 就和 right 一样了,endleft 也一样。这是需要注意的地方。

垂直对齐方式 textBaseline

使用 textBaseline 属性可以设置文字的垂直对齐方式。

在使用 textBaseline 前,需要自行了解 css 的文本基线。
在这里插入图片描述
用一张网图解释一下基线
textBaseline 可选属性:

  • alphabetic: 默认。文本基线是普通的字母基线。
  • top: 文本基线是 em 方框的顶端。
  • bottom: 文本基线是 em 方框的底端。
  • middle: 文本基线是 em 方框的正中。
  • hanging: 文本基线是悬挂基线。

在这里插入图片描述

红线是辅助参考线。

<canvas id="c" width="800" height="300" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 横向的辅助线(参考线,在画布中间)cxt.moveTo(0, 150)cxt.lineTo(800, 150)cxt.strokeStyle = 'red'cxt.stroke()cxt.font = '20px Arial'// 默认 alphabeticcxt.textBaseline = 'alphabetic'cxt.fillText('雷猴 alphabetic', 10, 150)// 默认 topcxt.textBaseline = 'top'cxt.fillText('雷猴 top', 200, 150)// 默认 bottomcxt.textBaseline = 'bottom'cxt.fillText('雷猴 bottom', 320, 150)// 默认 middlecxt.textBaseline = 'middle'cxt.fillText('雷猴 middle', 480, 150)// 默认 hangingcxt.textBaseline = 'hanging'cxt.fillText('雷猴 hanging', 640, 150)
</script>

注意:在绘制文字的时候,默认是以文字的左下角作为参考点进行绘制

图片

Canvas 中可以使用 drawImage() 方法绘制图片。

渲染图片

渲染图片的方式有2中,一种是在JS里加载图片再渲染,另一种是把DOM里的图片拿到 canvas 里渲染

渲染的语法:

drawImage(image, dx, dy)
  • image: 要渲染的图片对象。
  • dx: 图片左上角的横坐标位置。
  • dy: 图片左上角的纵坐标位置。

JS版

JS 里加载图片并渲染,有以下几个步骤:

  1. 创建 Image 对象
  2. 引入图片
  3. 等待图片加载完成
  4. 使用 drawImage() 方法渲染图片
    在这里插入图片描述
<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')// 1 创建 Image 对象const image = new Image()// 2 引入图片image.src = './images/dog.jpg'// 3 等待图片加载完成image.onload = () => {// 4 使用 drawImage() 方法渲染图片cxt.drawImage(image, 30, 30)}
</script>

DOM版

在这里插入图片描述

<style>#dogImg {display: none;}
</style><img src="./images/dog.jpg" id="dogImg"/>
<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')const image = document.getElementById('dogImg')cxt.drawImage(image, 70, 70)
</script>

因为图片是从 DOM 里获取到的,所以一般来说,只要在 window.onload 这个生命周期内使用 drawImage 都可以正常渲染图片。

本例使用了 css 的方式,把图片的 display 设置成 none 。主要担心被 <img> 影响到本例讲解。

设置图片宽高

前面的例子都是直接加载图片,图片默认的宽高是多少就加载多少。

如果需要指定图片宽高,可以在前面的基础上再添加两个参数:

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

image、 dx、 dy 的用法和前面一样。

dw 用来定义图片的宽度,dh 定义图片的高度。
在这里插入图片描述

<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')const image = new Image()image.src = './images/dog.jpg'image.onload = () => {cxt.drawImage(image, 30, 30, 100, 100)}
</script>

我把图片的尺寸设为 100px * 100px,图片看上去比之前就小了很多。

截取图片 仅展示某部分

截图图片同样使用drawImage() 方法,只不过传入的参数数量比之前都多,而且顺序也有点不一样了。

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

以上参数缺一不可

  • image: 图片对象
  • sx: 开始截取的横坐标
  • sy: 开始截取的纵坐标
  • sw: 截取的宽度
  • sh: 截取的高度
  • dx: 图片左上角的横坐标位置
  • dy: 图片左上角的纵坐标位置
  • dw: 图片宽度
  • dh: 图片高度

在这里插入图片描述

<canvas id="c" width="500" height="500" style="border: 1px solid #ccc;"></canvas><script>const cnv = document.getElementById('c')const cxt = cnv.getContext('2d')const image = new Image()image.src = './images/dog.jpg'image.onload = () => {cxt.drawImage(image, 0, 0, 100, 100, 30, 30, 200, 200)}
</script>

Canvas内部元素 的 事件监听

canvas 内部 图形元素 不能像DOM元素一样方便的添加交互事件监听,因为canvas内不存在“元素”这个概念,它们仅仅是canvas绘制出来的图形。
这对于交互开发来说是一个必经障碍,想要监听图形的点击事件思路很简单,只要监听canvas元素本身的点击事件,再判断点击坐标位于哪一个图形内部,就变相实现了图形点击事件。

监听图像上 点击事件 的基本流程:

  1. 通过getBoundingClientRect方法获取canvas相对于可视区域的偏移量
  2. canvas点击事件触发时使用clientX、clientY方法获取点击位置,计算可得到点击位置在canvas中的坐标
  3. 清空画布
  4. 重绘画布,在绘制完每个图形的时候都执行一次isPointInPath方法判断点击位置是否在该图形中,决定是否要执行onClick事件

从上面可以看出,最重要的是 确定 点击坐标位于哪一个图形内部
这里将介绍三种方法,判断坐标点是否位于某个canvas图形内部。

像素法

像素检测法的思路是,将canvas中的多个图形(如果有多个的话)分别离屏绘制,并用getImageData()方法分别获取到像素数据保存起来。当canvas元素监听到点击事件时,通过点击坐标可以直接推算出点击发生在canvas上的第几个像素,然后遍历前面保存的图形数据,看看这个像素的alpha值是不是0,如果是0说明落点不在当前图形内,否则就说明点到了这个图形。

根据点击坐标得到所点击的像素序号的方法:

像素序号 = (纵坐标-1) * canvas宽度 + 横坐标

比如在宽度为 5 的画布上点击坐标(3,3),根据上述公式得到像素序号是(3-1) * 5 + 3 = 18,如图所示:
在这里插入图片描述
因为canvas导出的图形数据是将每个像素以rgba的顺序存成4个数字组成的数组,所以想访问指定像素的alpha值,只要读取这个数组的第pIndex * 4 + 3个值就可以了,如果这个值不为0,说明该像素可见,也就是点击到了该图形。

这个方法是我认为思路最直接、结果最准确、而且对图形形状没有任何要求的方法,但这个方法有一个致命的局限,当图形需要在画布上移动时,要频繁的创建数据缓存才能保证检测结果准确,受到画布尺寸和图形数量的影响,getImageData()方法的性能会成为严重的瓶颈。所以如果canvas图形是静态的,这个方法非常适合,否则就不适合用这个方法了。

角度法

角度判断法的原理很容易理解,如果一个点在多边形内部,则该点与多边形所有顶点两两构成的夹角,相加应该刚好等于360°。
在这里插入图片描述
计算过程可以转变为以下三个步骤:

  1. 已知多边形顶点和已知坐标,将坐标与顶点两两组合成三点队列
  2. 已知三点求夹角,可以使用余玄定理
  3. 判断夹角之和是否360°
//计算两点距离
const getDistence = function (p1, p2) {return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y))
};
//角度法判断点在多边形内部
const checkPointInPolyline = (point, polylinePoints) => {let totalA = 0;const A = point;for (let i = 0; i < polylinePoints.length; i++) {let B, C;if (i === polylinePoints.length - 1) {B = {x: polylinePoints[i][0],y: polylinePoints[i][1]};C = {x: polylinePoints[0][0],y: polylinePoints[0][1]};} else {B = {x: polylinePoints[i][0],y: polylinePoints[i][1]};C = {x: polylinePoints[i + 1][0],y: polylinePoints[i + 1][1]};}//计算角度const angleA = Math.acos((Math.pow(getDistence(A, C), 2) + Math.pow(getDistence(A, B), 2) - Math.pow(getDistence(B, C), 2)) / (2 * getDistence(A, C) * getDistence(A, B)))totalA += angleA}//判断角度之和return totalA === 2 * Math.PI
}

这个方法有一个局限性,就是图形必须是凸多边形。如果不是凸多边形需要先切割成凸多边形再计算,这就比较复杂了。

射线法

射线法是非常好用的方法,只要判断点与多边形一侧的交点个数为 奇数 ,则点在多边形内部。
需要注意的是,只要数任何一侧的焦点个数就可以,比如左侧。
这个方法不限制多边形的类型,凸多边形、凹多边形甚至环形都可以。
在这里插入图片描述

const checkPointInPolyline = (point, polylinePoints) => {//射线法let leftSide = 0;const A = point;for (let i = 0; i < polylinePoints.length; i++) {let B, C;if (i === polylinePoints.length - 1) {B = {x: polylinePoints[i][0],y: polylinePoints[i][1]};C = {x: polylinePoints[0][0],y: polylinePoints[0][1]};} else {B = {x: polylinePoints[i][0],y: polylinePoints[i][1]};C = {x: polylinePoints[i + 1][0],y: polylinePoints[i + 1][1]};}//判断左侧相交let sortByY = [B.y, C.y].sort((a,b) => a-b)if (sortByY[0] < A.y && sortByY[1] > A.y){if(B.x<A.x || C.x < A.x){leftSide++}}}return leftSide % 2 === 1
}

射线法有一种特殊情况,当点在多变形的一条边上时需要特殊处理。但在实际项目中个人认为也可以不处理,因为如果用户刚好点在图形的边界上,那么程序认为他没有点到也是可以的。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode142. 环形链表 II(HOT100)

链接 我的错误代码&#xff1a; class Solution { public:ListNode *detectCycle(ListNode *head) {if(!head||!head->next)return nullptr;ListNode* f head->next,*s head;while(f){f f->next,s s->next;if(!f)return nullptr;f f->next;if(fs){ListNo…

centos安装小火车

平时没事闲着 装个小火车玩-------->>>>> yum install sl.x86_64 启动命令 sl 就会出现以下场景

JavaScript的let、var、const

这张图片主要介绍了JavaScript中的三种变量声明方式&#xff1a;let、var和const。 1. let 含义&#xff1a;let是现在实际开发中常用的变量声明方式。特点&#xff1a; 块级作用域&#xff1a;let声明的变量只在其所在的块级作用域内有效。例如&#xff1a;{let x 10; } co…

替换Nacos的MySQL驱动

前言&#xff1a;替换Nacos的MySQL驱动能实现使Nacos支持MySQL8.0及以上版本的MySQL数据库 注&#xff1a;下述教程会使用命令先解压Nacos的jar包然后重新用命令把Nacos压缩成jar包&#xff0c;不然直接用压缩工具替换MySQL驱动后的Nacos是会启动不起来的&#xff08;因为没有替…

蓝桥杯每日真题 - 第21天

题目&#xff1a;(空间) 题目描述&#xff08;12届 C&C B组A题&#xff09; 解题思路&#xff1a; 转换单位&#xff1a; 内存总大小为 256MB&#xff0c;换算为字节&#xff1a; 25610241024268,435,456字节 计算每个整数占用空间&#xff1a; 每个 32 位整数占用…

AI赋能电商:构建高效、智能化的新零售生态

随着人工智能&#xff08;AI&#xff09;技术的不断进步&#xff0c;其在电商领域的应用日益广泛&#xff0c;从购物推荐到供应链管理&#xff0c;再到商品定价&#xff0c;AI正在全面改变传统电商的运营模式&#xff0c;并推动行业向智能化和精细化方向发展。本文将探讨如何利…

Python酷库之旅-第三方库Pandas(237)

目录 一、用法精讲 1116、pandas.tseries.offsets.BusinessHour.is_year_end方法 1116-1、语法 1116-2、参数 1116-3、功能 1116-4、返回值 1116-5、说明 1116-6、用法 1116-6-1、数据准备 1116-6-2、代码示例 1116-6-3、结果输出 1117、pandas.tseries.offsets.Cu…

不一样的css(三)

目录 一、前言 二、五角星 1.五角星&#xff0c;叠盖法&#xff1a; 2.五角星&#xff0c;拼凑法&#xff1a; 3.五角星&#xff0c;svg画法&#xff1a; 4.五角星&#xff0c;利用clip-path属性进行裁剪 三、结束语 一、前言 通过上两节的内容我们对css画小图标有了新…

autogen框架中使用chatglm4模型实现react

本文将介绍如何使用使用chatglm4实现react&#xff0c;利用环境变量、Tavily API和ReAct代理模式来回答用户提出的问题。 环境变量 首先&#xff0c;我们需要加载环境变量。这可以通过使用dotenv库来实现。 from dotenv import load_dotenv_ load_dotenv()注意.env文件处于…

Neural Magic 发布 LLM Compressor:提升大模型推理效率的新工具

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

LabVIEW实现TCP/IP通信

目录 1、TCP通信原理 2、硬件环境部署 3、云端环境部署 4、TCP通信函数 5、程序架构 6、前面板设计 7、程序框图设计 8、测试验证 本专栏以LabVIEW为开发平台&#xff0c;讲解物联网通信组网原理与开发方法&#xff0c;覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合…

Linux系统Docker部署开源在线协作笔记Trilium Notes与远程访问详细教程

目录 ⛳️推荐 前言 1. 安装docker与docker-compose 2. 启动容器运行镜像 3. 本地访问测试 4.安装内网穿透 5. 创建公网地址 6. 创建固定公网地址 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下…

Linux关于vim的笔记

Linux关于vim的笔记&#xff1a;(vimtutor打开vim 教程) --------------------------------------------------------------------------------------------------------------------------------- 1. 光标在屏幕文本中的移动既可以用箭头键&#xff0c;也可以使用 hjkl 字母键…

Swift 实现链表重新排列:L0 → Ln → L1 → Ln-1

前言 本题由于没有合适答案为以往遗留问题&#xff0c;最近有时间将以往遗留问题一一完善。 143. 重排链表 不积跬步&#xff0c;无以至千里&#xff1b;不积小流&#xff0c;无以成江海&#xff0c;Swift社区 伴你前行。如果大家有建议和意见欢迎在文末留言&#xff0c;我们会…

C++ —— 以真我之名 如飞花般绚丽 - 智能指针

目录 1. RAII和智能指针的设计思路 2. C标准库智能指针的使用 2.1 auto_ptr 2.2 unique_ptr 2.3 简单模拟实现auto_ptr和unique_ptr的核心功能 2.4 shared_ptr 2.4.1 make_shared 2.5 weak_ptr 2.6 shared_ptr的缺陷&#xff1a;循环引用问题 3. shared_ptr 和 unique_…

Macos远程连接Linux桌面教程;Ubuntu配置远程桌面;Mac端远程登陆Linux桌面;可能出现的问题

文章目录 1. Ubuntu配置远程桌面2. Mac端远程登陆Linux桌面3. 可能出现的问题1.您用来登录计算机的密码与登录密钥环里的密码不再匹配2. 找不到org->gnome->desktop->remote-access 1. Ubuntu配置远程桌面 打开设置->共享->屏幕共享。勾选允许连接控制屏幕&…

ElasticSearch学习了解笔记

搜索引擎的原理&#xff1a; 1、查询分析&#xff08;自然语言处理&#xff09;理解用户需求 2、分词技术 3、关键词搜索匹配 4、搜索排序 lucence Lucene 是一个成熟的权威检索库 Elasticsearch 的搜索原理简单过程是&#xff0c;索引系统通过扫描文章中的每一个词&#xff…

GoF设计模式——结构型设计模式分析与应用

文章目录 UML图的结构主要表现为&#xff1a;继承&#xff08;抽象&#xff09;、关联 、组合或聚合 的三种关系。1. 继承&#xff08;抽象&#xff0c;泛化关系&#xff09;2. 关联3. 组合/聚合各种可能的配合&#xff1a;1. 关联后抽象2. 关联的集合3. 组合接口4. 递归聚合接…

【C++】C++11新特性详解:可变参数模板与emplace系列的应用

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与QueuePriori…

多模态大型语言模型(MLLM)综述

目录 多模态大语言模型的基础 长短期网络结构(LSTM) 自注意力机制 基于Transformer架构的自然语言处理模型 多模态嵌入的关键步骤 TF-IDF TF-IDF的概念 TF-IDF的计算公式 TF-IDF的主要思路 TF-IDF的案例 训练和微调多模态大语言模型(MLLM) 对比学习 (CLIP, ALIG…