最常用的 9 个JavaScript 函数与示例

news2024/9/26 5:18:47

输出内容才能更好的理解输入的知识

前言🎀

如果你想深入图形、可视化等领域,那么肯定离不开 canvas、webgl、three.js 等一系列技术。在这众多技术中,选择canvas2d为基础来入门是一个不错的选择。

canvas在动画、可视化、图片处理等领域有着出人意料的表现,是十分值得学习的一门技术。例如echarts就是通过canvas和svg实现的。

希望通过这篇文章记录Canvas基本的用法和场景,并拓展自己的前端视野。

如果觉得有收获还望大家点赞、收藏🌹

案例

先来看几个案例,你可能就对canvas的应用场景有所了解,也会对它产生兴趣。

当然有些案例不使用canvas一样能实现,但在都能实现目标的情况下如果canvas效果更好或者更方便,那么还是建议大家尝试下canvas的~

点击小球很简单的一个小球动画,能衍生出许多东西,我为什么想到了合成大西瓜hhhh

截图还有很多额外操作,如滤镜、调色等就像P图一样。略微修改一下还能当放大镜使用

粒子爆炸****particle还有特别多场景如 画板、图片水印、视频、进度条、粒子动画…都有很好的效果,但没有研究就不详述了,欢迎了解的同学补充。

Canvas

首先我们学习如何创建canvas元素并获取操作对象

创建画布

是一个可以_使用脚本 (通常为JavaScript) 来绘制图形的 HTML 元素_ 。例如,它可以用于绘制图表、制作图片构图或者制作简单的动画。

<html>....<canvas id="canvas" width="300" height="300"></canvas>....
</html> 

这样就在页面上创建了一个canvas画布,需要注意的是canvas元素自身_只有两个属性 width和height_,都只接受数字为属性值,无需设置单位,默认单位为像素。

如果通过css来定义大小,绘制时图像会伸缩以适应框架的尺寸,如果css的尺寸与初始画布比例不一样,内容就容易出现拉伸。

直线被拉粗

获取上下文

元素创造了一个固定大小的画布,它公开了一个或多个_渲染上下文_,其可以用来绘制和处理要展示的内容。我们进行操作时先根据需要获取渲染上下文,然后在它上面绘制。

这个过程就像我们在进行绘画之前选择了一只画笔。

<script>....// 获取canvas对象const canvas = document.getElementById('canvas')// 获取渲染上下文对象const ctx = canvas.getContext('2d')...
</script> 

本文主要了解2D渲染上下文,canvas也支持如WebGL的3D上下文,更多详情 MDN-Canvas

Canvas API

在创建完canvas画布并获取到渲染上下文拿到画笔后,我们可以看看canvas支持了哪些操作。

以下内容主要总结自MDN-Canvas-API与部分使用经验,适当精简。如果对具体细节有疑惑的同学可以查看官方文档或评论区交流。

栅格

通常canvas元素默认被_网格覆盖_,一个网格单元相当于canvas元素中的一像素。所有元素都相对于原点(0, 0)定位,图中蓝色正方形距离Y轴x像素,距离X轴y像素,所以坐标为(x, y)。

绘制图形

先学习简单的如何绘制直线、矩形、三角形和圆形等。但在绘制前需要掌握路径,它是图形形成的基础。

路径 Path

图形的基本元素是 路径 。路径 是通过不同颜色和宽度的线段或曲线相连形成的不同形状的_点的集合_。一个路径,甚至一个子路径,都是_闭合的_。

主要使用以下四个函数:

1.beginPath _新建_一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
2.closePath _闭合_路径之后图形绘制命令又重新指向到上下文中。
3.stroke 通过_线条_来绘制图形轮廓。
4.fill 通过填充路径的内容区域生成_实心_的图形。

生成路径主要有三个步骤:

1.调用 beginPath() 清空重置路径和子路径列表,以重新绘制图形。
2.调用 目标函数 绘制路径。
3.非必需,调用 closePath() 绘制一条从当前点到开始点的直线来闭合图形。

如果不按照规定步骤来绘制路径可能会导致意外的结果我们通过绘制圆弧的案例来观察(arc函数用于绘制圆和圆弧等图形)

移动笔触 moveTo

每次绘制总是从一个点开始再到另一个点结束,可以想象一下我们在纸上移动画笔的过程。

而canvas则提供了一个 moveTo函数 让我们在“画布”上“移动”自己画笔的位置(并不是作画,而是设置起始点)。

moveTo(x, y):将笔触移动到指定的坐标 x 以及 y 上

直线 lineTo

直线也是图形的一种,学习了路径和移动笔触后再来看如何绘制出一条直线主要使用 lineTo(x, y) 函数,绘制一条_从当前位置到指定x以及y位置的直线_

<script>....ctx.beginPath()ctx.moveTo(50, 50)ctx.lineTo(200, 50)ctx.closePath()ctx.stroke()
</script> 

三角形 triangle

三角形可以理解为三条直线连接在一起,

<script>....ctx.moveTo(50, 50)ctx.lineTo(100, 50)ctx.lineTo(100, 100)ctx.lineTo(50, 50)ctx.stroke()
</script> 

也可以是一次路径绘制上的两条直线通过闭合连接头尾

<script>....ctx.beginPath()ctx.moveTo(50, 50)ctx.lineTo(100, 50)ctx.lineTo(100, 100)ctx.closePath()ctx.stroke()
</script> 

矩形 rect

所有的图形都能由单条或者多条路径组合而成,不过canvas也提供了内置的方法让我们快速绘制复杂的图形。

我们先来看矩形的绘制,它可以由四条直线拼接而成,但更多时候我们直接使用内置方法。

主要有以下三个方法,其中 x 和 y 是矩形的起点坐标,width 和 height 设置矩形的尺寸。

1.fillRect(x, y, width, height) 绘制一个填充的矩形

2.strokeRect(x, y, width, height) 绘制一个矩形的边框

3.clearRect(x, y, width, height) 清除指定矩形区域,让清除部分完全透明

圆形 arc

绘制圆形的方法是:arc(x, y, radius, startAngle, endAngle, anticlockwise)

其中(x,y)为圆心,radius 为圆的半径,(startAngle,endAngle) 为开始、结束角度, anticlockwise 为绘制方向(默认false为顺时针)。

需要注意的是:

startAngle,endAngle_都以弧度为单位_ 每1°为Math.PI / 180

所以为便于理解设置值时应参照公式: 弧度 = 目标角度 * (Math.PI / 180)

即 360° = 360 * (Math.PI / 180)

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
ctx.arc(100, 100, 50, 0, 360 * Math.PI / 180, false)
ctx.stroke()
ctx.closePath()

ctx.beginPath()
ctx.arc(250, 100, 50, 0, 180 * Math.PI / 180, false)
ctx.stroke()
ctx.closePath() 

注意:每次绘制完后要闭合路径,否则会导致路径里举例的情况

样式、颜色

在上一节里只用到了默认的线条和填充样式,这一节来学习canvas更多的样式可选项,绘制出更吸引人的内容。

可以想象下我们使用不同色彩、大小的笔和不同的手法在画布上绘画。

色彩 Colors

canvas除了提供绘制内容的方法,也提供了设置图形颜色的属性。

属性值支持符合CSS所有color属性的值:

// 这些 fillStyle 的值均为 '橙色'
ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,1)"; 

主要有以下两个属性:

1.fillStyle = color 设置图形填充颜色

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
for (let i = 0; i < 6; i++){for (let j = 0; j < 6; j++){// 设置填充颜色ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor(255 - 42.5 * j)}, 0)`ctx.fillRect(100 + j * 30, 15 + i * 30, 30, 30)}
} 

2.strokeStyle = color 设置图形轮廓颜色

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
for (let i=0;i<6;i++){for (let j=0;j<6;j++){// 设置轮廓颜色ctx.strokeStyle = `rgb(0, ${Math.floor(255 - 42.5 * i)}, ${Math.floor(255 - 42.5 * j)})`ctx.beginPath();ctx.arc(100 + j * 25,30 + i * 25, 10, 0, 360 * Math.PI / 180, true);ctx.stroke();}
} 

透明度 Transparency

除了可以绘制实色图形,我们还可以用 canvas 来绘制半透明的图形。通过设置 globalAlpha 属性或者使用一个半透明颜色作为轮廓或填充的样式。

globalAlpha = transparencyValue

transparencyValue 有效的值范围是 0.0(完全透明)到 1.0(完全不透明),默认是 1.0。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'
ctx.fillRect(50, 20, 250, 30)

ctx.beginPath()
ctx.fillStyle = 'rgba(0, 0, 0, 1)'
ctx.globalAlpha = 0.3
ctx.fillRect(50, 50, 250, 30)

ctx.beginPath()
ctx.fillStyle = 'green'
ctx.globalAlpha = 0.4
ctx.fillRect(150, 30, 50, 40) 

线型 Line Styles

可以通过以下一系列属性来设置线的样式。

1.lineWidth = value 设置_线条宽度_

这个属性设置当前绘线的粗细。属性值必须为正数。默认值是 1.0。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
ctx.lineWidth = 1.0
ctx.moveTo(50, 50)
ctx.lineTo(200, 50)
ctx.stroke()

ctx.beginPath()
ctx.lineWidth = 2.0
ctx.moveTo(50, 75)
ctx.lineTo(200, 75)
ctx.stroke()

ctx.beginPath()
ctx.lineWidth = 4.0
ctx.moveTo(50, 100)
ctx.lineTo(200, 100)
ctx.stroke()

ctx.beginPath()
ctx.lineWidth = 10.0
ctx.moveTo(50, 125)
ctx.lineTo(200, 125)
ctx.stroke() 

2.lineCap = type 设置_线条末端样式_

属性 lineCap 的值决定了_线段端点_显示的样子。它可以为下面的三种的其中之一:butt无线帽round圆线帽square方形线帽。默认是 butt。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
ctx.strokeStyle = '#09f';
ctx.beginPath();
ctx.moveTo(50,30);
ctx.lineTo(50,120);
ctx.moveTo(250,30);
ctx.lineTo(250,120);
ctx.stroke();
// butt
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.lineWidth = 8.0
ctx.lineCap = 'butt'
ctx.moveTo(50, 50)
ctx.lineTo(250, 50)
ctx.stroke()
// round
ctx.beginPath()
ctx.lineCap = 'round'
ctx.moveTo(50, 75)
ctx.lineTo(250, 75)
ctx.stroke()
ctx.closePath()
// square
ctx.beginPath()
ctx.lineCap = 'square'
ctx.moveTo(50, 100)
ctx.lineTo(250, 100)
ctx.stroke() 

3.lineJoin = type 设定线条与_线条间接合处的样式_

使用与lineCap类似但作用的位置是线段连接处。

lineJoin 的属性值决定了图形中两_线段连接处_所显示的样子。它可以是这三种之一:round圆角, bevel斜角miter尖角。默认是 miter。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const lineJoin = ['round', 'bevel', 'miter'];
ctx.lineWidth = 10;
for (var i = 0; i < lineJoin.length; i++) {ctx.lineJoin = lineJoin[i];ctx.beginPath();ctx.moveTo(90, 10 + i * 60)ctx.lineTo(180, 10 + i * 60)ctx.lineTo(180, 60 + i * 60)ctx.stroke()
} 

4.setLineDash(segments) 设置当前虚线样式 / lineDashOffset = value 设置虚线样式的起始偏移量

setLineDash 方法接受一个数组,来指定线段与间隙的交替;

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
ctx.setLineDash([4, 2]);
ctx.strokeRect(10,10, 100, 100); 

lineDashOffset 属性设置起始偏移量。

渐变 Gradients

就好像一般的绘图软件一样,我们可以用_线性_或_径向_的渐变来_填充_或_描边_,主要使用_canvasGradient对象_,并且赋值给图形的 fillStyle 或 strokeStyle 属性。

addColorStop

可以使用_addColorStop_方法对canvasGradient渐变对象上色。

addColorStop 每次调用会为渐变对象添加一个色标,渐变内容会从一个色标的颜色渐变为下一个色标的颜色,可以存在多个色标。

**addColorStop(position, color)**接收两个参数,分别表示:1.position 渐变中颜色所在_相对位置_, 如0代表颜色出现在开头 1代表出现在末尾2.color 色标值,必须是一个有效的CSS颜色值,如#fff rgba(0, 0, 0, 1)等

canvasGradient

使用以下两个方法创建 canvasGradient渐变对象,并使用addColorStop方法对其上色

1.createLinearGradient(x1, y1, x2, y2) 创建_线性渐变对象_,参数分别表示 渐变的起点坐标(x1, y1) 和 渐变的终点坐标(x2, y2)。如果两个坐标x/y轴坐标不同会出现沿x/y轴的倾斜

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const gradient1 = ctx.createLinearGradient(20, 40, 280, 40)
gradient1.addColorStop(0, '#03a9f4')
gradient1.addColorStop(0.33, '#f441a5')
gradient1.addColorStop(0.66, '#ffeb3b')
gradient1.addColorStop(1, '#03a9f4')
ctx.beginPath()
ctx.fillStyle = gradient1
ctx.fillRect(20, 40, 260, 40)

const gradient2 = ctx.createLinearGradient(20, 120, 280, 200)
gradient2.addColorStop(0, '#03a9f4')
gradient2.addColorStop(0.33, '#f441a5')
gradient2.addColorStop(0.66, '#ffeb3b')
gradient2.addColorStop(1, '#03a9f4')
ctx.beginPath()
ctx.fillStyle = gradient2
ctx.fillRect(20, 120, 260, 40) 

2.createRadialGradient(x1, y1, r1, x2, y2, r2) 创建_径向渐变对象_,参数分别表示 开始圆的坐标 (x1, y1) 和 半径r1 ,以及 结束圆的坐标(x2, y2) 和 半径r2。想象一个圆1沿着路径逐渐变为另一个圆2,或者以圆2为中心向外围的圆1扩张。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const radgrad1 = ctx.createRadialGradient(45,45,10,52,50,30);
radgrad1.addColorStop(0, '#A7D30C');
radgrad1.addColorStop(0.9, '#019F62');
radgrad1.addColorStop(1, 'rgba(1,159,98,0)');

const radgrad2 = ctx.createRadialGradient(105,105,20,112,120,50);
radgrad2.addColorStop(0, '#FF5F98');
radgrad2.addColorStop(0.75, '#FF0188');
radgrad2.addColorStop(1, 'rgba(255,1,136,0)');

const radgrad3 = ctx.createRadialGradient(95,15,15,102,20,40);
radgrad3.addColorStop(0, '#00C9FF');
radgrad3.addColorStop(0.8, '#00B5E2');
radgrad3.addColorStop(1, 'rgba(0,201,255,0)');

ctx.beginPath()
ctx.fillStyle = radgrad1
ctx.fillRect(0, 0, 300, 200)
ctx.fillStyle = radgrad2
ctx.fillRect(0, 0, 300, 200)
ctx.fillStyle = radgrad3
ctx.fillRect(0, 0, 300, 200) 

阴影 Shadows

可以给文字或图形添加阴影,主要使用以下几个属性:

1.shadowOffsetX = float / shadowOffsetY = float 用来设定阴影在 X 和 Y 轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为 0。
2.shadowBlur = float 用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0。
3.shadowColor = color 是标准的 CSS 颜色值,用于设定阴影颜色效果,默认是全透明的黑色。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const radgrad1 = ctx.createRadialGradient(45,45,10,52,50,30);

ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 2;
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";

ctx.font = "20px Times New Roman";
ctx.fillStyle = "Black";
ctx.fillText("Sample String", 80, 30)

ctx.shadowOffsetX = -10;
ctx.shadowOffsetY = -10;
ctx.shadowBlur = 3;
ctx.shadowColor = "orange";

ctx.font = "20px Times New Roman";
ctx.fillStyle = "Black";
ctx.fillText("Sample String", 80, 80) 

绘制文本

在了解样式、颜色后再来看如何在canvas中绘制文本。

绘制方式

canvas提供了两种方式来渲染文本:

1.fillText(text, x, y, maxWidth) 在 (x,y) 位置以_填充_的方式绘制指定的文本,绘制的最大宽度是可选的。2.strokeText(text, x, y, maxWidth) 在 (x,y) 位置以_描边_的方式绘制指定的文本,绘制的最大宽度是可选的。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

ctx.font = '40px serif'
ctx.fillText('hello world', 40, 80)
ctx.strokeText('hello world', 40, 120) 

文本样式

之前的例子中已经有使用文本样式了,例如font可以调整文本的字体大小和类型。还有以下更多的属性可以改变canvas显示文本的方式:

1.font = value 设置文本样式的属性,value值与CSS font属性相同,默认为"10px sans-serif"

// canvans font支持CSS font所有属性
cxt.font = 'font-style font-variant font-weight font-size/line-height font-family'
// font-family需要与font-size同时设置,否则无效
ctx.font = '20px serif' 

2.textAlign = value 文本对齐选项,可选值为:start 坐标x处开始、 end 坐标x处结束left 左对齐、 right 右对齐 和 center 居中。默认值是start。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
ctx.moveTo(200, 0)
ctx.lineTo(200, 200)
ctx.stroke()

ctx.font = '20px serif'
const aligns = ['center', 'left', 'right', 'start', 'end']
aligns.forEach((align, index) => {ctx.textAlign = alignctx.fillText('文本 ' + align, 200, 30 + 30 * index)
}) 

3.textBaseline = value 基线对齐选项,可选值为:top、 middle、 bottom、alphabetic、 hanging 和 ideographic。默认值是alphabetic。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

ctx.font = '18px serif'
const baslines = ['top', 'middle', 'bottom', 'alphabetic', 'hanging', 'ideographic']
baslines.forEach((basline, index) => {// 绘制对比线ctx.moveTo(0, 30.5 + 40 * index)ctx.lineTo(300, 30.5 + 40 * index)ctx.stroke()// 调整基准线ctx.textBaseline = basline// 绘制文本ctx.fillText('文本 ' + basline, 100, 30 + 40 * index)
}) 

4.direction = value 描述文本方向的属性。可能的值包括:ltr 从左向右、 rtl 从右向左inherit 继承父元素。默认值是inherit。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

ctx.font = '18px serif'
const directions = ['ltr', 'rtl', 'inherit']
directions.forEach((direction, index) => {ctx.beginPath()ctx.direction = direction// 绘制文本ctx.fillText('canvas ' + direction + '!', 140, 30 + 40 * index)
}) 

这里不知为什么rtl只反序了符号,还望大佬指点

操作图片

canvas对图片(包括但不限于 图片、视频,甚至是另一个canvas)各种操作的支持是一大亮点,这也是非常让人感兴趣的一个地方,感觉终于摆脱了条条框框的线条和图形。

canvas提供了 绘制图片 和 获取图片像素 的方法

获取图片

在绘制图片或者获取图片信息用于操作之前,首先要_获取目标图片源_。

浏览器支持的_任意格式的外部图片_都可以使用,比如 PNG、GIF 或 JPEG。

还可以将*中的视频帧*作为图片源。

甚至可以将同一个页面中_其他 canvas 元素_生成的图片作为图片源。(例如案例中的截图

外部图片主要通过两种方式:1.获取页面上的DOM-img元素 2.在JS里创建Image对象 onload回调时读取

<html><div id="app"><img id="img1" src="https://p3-passport.byteimg.com/img/user-avatar/edcdbbde0d6f5cb89d7c28187ed55480~180x180.awebp" /><canvas id="canvas" width="300" height="200"></canvas></div>
</html>
<script> const canvs = document.getElementById('canvas')const ctx = canvas.getContext('2d')const img1 = document.getElementById('img1')const img2 = new Image()img2.src = 'https://p3-passport.byteimg.com/img/user-avatar/edcdbbde0d6f5cb89d7c28187ed55480~180x180.awebp'img2.onload(() => {// ...}) </script> 

引用其它canvas元素主要通过:1.document.getElementsByTagName2.document.getElementById

但你引入的应该是已经准备好的canvas。

一个常用的应用就是将第二个 canvas 作为另一个大的 canvas 的缩略图。

绘制图片

一旦获得了源图对象,我们就可以使用 drawImage 方法将它渲染到 canvas 里。drawImage 方法有三种形态:

drawImage(image, dx, dy) 绘制

_参数_1. image:图片对象2. dx:图片左上角在canvas中的x轴坐标3. dy:图片左上角在canvas中的y轴坐标

效果

最普通的渲染图片方式,将目标图片渲染到canvas的(x, y)坐标上。超出canvas的部分会被隐藏,重叠的部分新的图片在上层。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

const img = new Image()
img.src = 'https://p3-passport.byteimg.com/img/user-avatar/edcdbbde0d6f5cb89d7c28187ed55480~180x180.awebp'
img.onload = () => {ctx.drawImage(img, 10, 10)ctx.drawImage(img, 40, 10)ctx.drawImage(img, 300 + img.width, 10)ctx.drawImage(img, 10, 100 + img.height)
} 
drawImage(image, dx, dy, dWidth, dHeight) 缩放

新增了两个参数用于_控制图像在canvas中的宽高_,两个参数都是可选的,但不能单独选择,如设置了width则必须设置height。

_参数_1. image:图片对象2. dx:图片左上角在canvas中的x轴坐标3. dy:图片左上角在canvas中的y轴坐标4. dWidth:图片在canvas中的宽度5. dHeight:图片在canvas中的高度

效果

将目标图片以dWidth的宽度和dHeight的高度渲染到canvas的(x, y)坐标上,dWidth/dHeight !== img.width/img.height 时会拉伸图片。

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) 裁剪

新增了四个参数用于控制在原图片上进行切片的位置和大小,需要注意的是这四个参数是插入在前面的。

_参数_1. image:图片对象2. sx:裁剪位置在图片对象中的x轴坐标3. sy:裁剪位置在图片对象中的y轴坐标4. sWidth:裁剪出的切片宽度5. sHeight:裁剪出的切片高度// 此处是由图片裁剪出的切片6. dx:切片左上角在canvas中的x轴坐标7. dy:切片左上角在canvas中的y轴坐标8. dWidth:切片在canvas中的宽度9. dHeight:切片在canvas中的高度

效果_首先在图片(sx, sy)位置裁剪出一个sWidth宽和sHeight高的_切片,然后将切片以dWidth的宽度和dHeight的高度渲染到canvas的(x, y)坐标上。

const canvs = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

const img = new Image()
img.src = 'https://p3-passport.byteimg.com/img/user-avatar/edcdbbde0d6f5cb89d7c28187ed55480~180x180.awebp'
img.onload = () => {ctx.drawImage(img, 10, 10)const { width, height } = imgconst sWidth = 0.5 * widthconst sHeight = 0.5 * heightconst dWidth = 0.8 * widthconst dHeight = 0.8 * heightconst positions = [[0, 0], [0.5, 0], [0, 0.5], [0.5, 0.5]]for (let [sx, sy] of positions) {const dx = 50 + width + 2.1 * sx * dWidthconst dy = 10 + 2.1 * sy * dHeightctx.drawImage(img, sx * width,sy * height, sWidth, sHeight, dx, dy, dWidth, dHeight)}
} 

像素操作

canvas除了支持对图片缩放、裁剪外,还提供了获取图片像素的功能,这衍生出了更多对图片的操作。

通过canvas的_getImageData方法_可以获得_ImageData对象_,而ImageData对象对象中存储着canvas对象真实的像素数据。

getImageData(left, top, width, height)

_作用_该方法会解析canvas中 以(left, top)为坐标点 宽width、高height 的画布区域,并返回一个ImageData对象,它代表了画布指定区域的数据。

_备注_任何在画布以外的元素都会被返回成一个透明黑的 ImageData 对像。

ImageData

_作用_可以通过ImageData对象操纵像素数据,直接读取或将数据数组写入该对象中。

属性_1.width 图片宽度,单位像素。2.height 图片高度,单位像素。3.data Uint8ClampedArray类型的一维数组,包含了指定区域里_每个像素点的RGBA格式的整型数据,范围在0至255之间(包括255)。

Uint8ClampedArray 8位无符号整型(即值区间在[0, 255] = [0, (2 ^ 8 - 1)]的正整数)固定数组,区间外的值会被替换成0/255,非整数的值会被替换成最接近它的整数。

∵ 每一个像素点有4个值占据data数组4个索引位置,对应像素rgba(R, G, B, A)的四个值∴ 第n个位置像素的R、G、B、A信息索引分别为:

Rn = (n - 1) * 4 
Gn = (n - 1) * 4 + 1
Bn = (n - 1) * 4 + 2
An = (n - 1) * 4 + 3 

∵ 图片的像素并不是一维数组一样平铺的,而是一个包含行和列的二维矩形∴ 我们在寻找目标像素点时一般不直接用n索引,而是用 i列j行 / [i, j] 来计算目标索引:

const width = img.width// 获取图片宽度 = 二维矩形的宽度
Rij = [(j - 1) * width + (i - 1)] * 4 
Gij = [(j - 1) * width + (i - 1)] * 4 + 1
Bij = [(j - 1) * width + (i - 1)] * 4 + 2
Aij = [(j - 1) * width + (i - 1)] * 4 + 3 

动画

canvas的动画主要是通过 在一些定时方法中去执行重绘操作实现的。

但为什么要重绘呢?

canvas的图像一旦绘制出来,它就是一直保持那样了。

∴ 如果需要移动它,我们不得不对所有东西(包括之前的)进行_重绘_。

而动画是一帧一帧连续绘制形成的,所以canvas实现动画的过程通常是 清理->绘制->清理->绘制… 不断重复的过程。

又为什么要在定时方法执行呢?

∵ 参考浏览器的事件循环机制,同步代码仅仅在脚本执行结束后才能看见结果,比如在for循环里完成动画是不太可能的。

∴ 我们需要通过一些定时执行的方法去调用重绘,实现动画的操控。如 setTimeOut、setInterval、requestAnimationFrame。

关于事件循环和定时器,可以看我之前的文章或者其他大佬的文章。

实现动画的步骤

可以通过以下步骤实现动画的一帧:1.清空 canvas 除非接下来要画的内容会完全充满 canvas(例如背景图),否则你需要清空所有。最简单的做法就是用 clearRect 方法。2.保存 canvas 状态 如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。3.绘制动画图形(animated shapes)  这一步才是重绘动画帧。4.恢复 canvas 状态 如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。

结语🎉

canvas在图形、动画、可视化等领域有出色的表现,入门即可轻松实现许多传统开发难以实现或实现复杂的场景,是前端扩展技术栈的不二之选。

第一次写基础入门文章,感觉对结构和节奏的把握还是不够😥。写到中途感到内容过多难以掌控,故省略了部分过于精细、乏味或偏僻的内容。

后续会持续更新canvas的进阶使用,欢迎大家关注第一时间收到更新消息哦😊

写作不易,如果觉得有收获还望大家点赞、收藏🌹

才疏学浅,如果对canvas的用法和知识有更多理解欢迎大家指教。

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

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

相关文章

物联网通信技术原理 第5章

目录 5.1 移动通信的基本概念及发展历史 5.1.1 移动通信的基本概念 5.1.2 移动通信的发展历史&#xff08;理解&#xff09; 1.第一代移动通信系统(1G) 2.第二代移动通信系统(2G) 3.第三代移动通信系统(3G) 5.1.3 移动通信的发展趋势与展望 5.2 无线传播与移动信道 5.2…

哈希的应用:布隆过滤器(C++实现)

文章目录1. 布隆过滤器1.1 背景1.2 概念1.3 控制误判率2. 实现布隆过滤器2.1 布隆过滤器类2.2 Set2.3 Test2.4 删除3. 优点4. 缺陷4. 缺陷1. 布隆过滤器 1.1 背景 位图&#xff08;bitmap算法&#xff09;告诉我们&#xff0c;想判断一个元素是否存在于某个集合中&#xff0c…

c语言指针 字符 字符串

1、sizeof 某个类型或者某个变量在内存中占用字节数。 例如&#xff1a;sizeof(int) ; sizeof(i)&#xff1b;都可以使用 2、运算符& 获取变量的地址。 int i; scanf("%d",&i); 输入变量时&#xff0c;必须使用&运算符。 &操作符只能应…

机器学习100天(二):002 数据预处理之导入数据集

机器学习 100 天,今天讲的是:数据预处理之导入数据集。 首先,我们打开 spyder。新建一个 load_data.py 脚本。 第一步,导入标准库 机器学习常用的标准库有 3 个: 第一个:numpy,用于数据处理。 第二个:matplotlib.pyplot,用于画图。 第三个:pandas,用于数值分析…

python 爬虫

前言 一、什么是爬虫 爬虫&#xff1a;&#xff08;又称为网页蜘蛛&#xff0c;网络机器人&#xff0c;在 FOAF 社区中间&#xff0c;更经常地称为网页追逐者&#xff09;&#xff1b;它是一种按照一定的规则&#xff0c;自动地从互联网上抓取对于我们有价值的网络信息的程序…

最强大的布局方案——网格Grid布局万字详解

Grid 布局又称网格布局&#xff0c;是W3C提出的一个二维布局系统&#xff0c;它与 Flex 布局有一定的相似性&#xff0c;都可以指定容器内部多个项目的位置。但它们也存在重大区别。Flex 布局是轴线布局&#xff0c;只能指定"项目"针对轴线的位置&#xff0c;可以看作…

jsp+ssm计算机毕业设计大学城二手书交易网站【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

绝对神器,今天教你如何识别图片上竖排的日语文字

在文字翻译或者其他的工作中我们经常遇到竖排的日语&#xff0c;有时候我们用普通的日语识别的软件根本无法完成 这个时候我们就需要一款可以识别竖排的日语工具&#xff0c;横排的我们很容易就能找到&#xff0c;但是竖排的就无能为力了 今天我们讲下如何识别竖排日语识别&a…

ZERO-SHOT:多聚焦融合

ZERO-SHOT MULTI-FOCUS IMAGE FUSION &#xff08;零镜头多焦点图像融合&#xff09; 多聚焦图像融合 (Multi-focus image fusion (MFIF)) 是消除成像过程中产生的离焦模糊的有效方法。The difficulties in focus level estimation and the lack of real training set for su…

计算机毕业设计springboot+vue文体用品商城网站

项目介绍 在当今社会的高速发展过程中,产生的劳动力越来越大,提高人们的生活水平和质量,尤其计算机科技的进步,数据和信息以人兴化为本的目的,给人们提供优质的服务,其中网上购买商品尤其突出,使我们的购物方式发生巨大的改变。而线上购物,不仅需要在硬件上为人们提供服务网上购…

ASPICE详细介绍-3.ASPICE有多少能力等级?

目录ASPICE有多少能力等级&#xff1f;9 个过程属性过程属性评定过程能力等级模型ASPICE有多少能力等级&#xff1f; ASPICE能力等级从0级到5级共分为6个层次&#xff0c;必须满足前一级别才可晋级下一个级别的评估。 【0级】Incomplete&#xff0c;未完成。 The process is…

【YOLOv7-环境搭建】PyTorch安装后输出版本显示No module named ‘Torch’的解决方法

可能一&#xff1a;PyCharm环境导入错误 配置的解释器&#xff0c;必须为所创建的虚拟环境下的python.exe文件&#xff0c;别的路径下的python.exe文件不好使&#xff01;&#xff01; 解决方法&#xff1a;根据【YOLOv7-环境搭建③】PyCharm安装和环境、解释器配置文中配置解…

微信小程序自定义头部导航nav

1.封装自定义nav导航组件 // app.js App({globalData: {systeminfo: false, //系统信息headerBtnPosi: false //头部菜单高度} })// components/nav/nav.js const app getApp(); Component({properties: {vTitle: { // 标题type: String,value: ""},isSearch: {…

大厂频频裁员,0基础转行做IT是不是已经晚了

现在转行做程序员是不是已经晚了 转行不会晚&#xff0c;晚的是你数不清的犹豫 对于二十来岁刚毕业或者毕业没几年的人来说&#xff0c;经历过社会的“摧残”&#xff0c;面对着一眼能够望到头的工作&#xff0c;拿着也不太高的工资&#xff0c;总是会去寻求一些改变与其每天…

Ajax请求原理与数据抓取

有些时候&#xff0c;我们直接通过网络请求库请求网页地址时&#xff0c;得到的响应结果可能跟浏览器中右键查看网页源码所看到的内容不一样。例如&#xff0c;在抓取&#xff1a;https://www.feeair.com/threeCode.html &#xff08;飞啊网&#xff09;这个网页时&#xff0c;…

公司固定资产管理系统

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 模块划分&#xff1a;公告类型、公告信息、员工信息、仓库信息、资产类型、资产信息、供应商信 息、采购信息、盗产调拨…

(Matlab)基于蝙蝠算法实现电力系统经济调度

目录 摘要&#xff1a; 1.蝙蝠优化算法的基本原理&#xff1a; 2.蝙蝠优化算法的流程&#xff1a; 3.仿真实验分析&#xff1a; 摘要&#xff1a; 基于Matalb平台&#xff0c;构建基于蝙蝠活动行为的蝙蝠优化算法&#xff0c;对一个含有6个火电机组的电力系统进行优化调度…

毕业设计 ESP32在线墨水屏桌面摆件 -物联网 单片机 嵌入式

0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过…

单因子分析(如何判定一个因子是否有效)

本人之前都是做期权中性策略&#xff0c;第一次接触这个多因子策略&#xff0c;和一些大私募对接学习后&#xff0c;才知道这里面的水&#xff08;只能说各有各的道&#xff09;。 先说下&#xff0c;何为因子策略&#xff0c;就是一个因子和股票的价格在一定时间内是存在一定的…

【算法数据结构专题】「线程锁算法专项」初探CLH队列锁机制原理分析

技术扩展 SMP&#xff08;对称多处理器架构&#xff09; SMP(Symmetric Multi-Processor)&#xff0c;即对称多处理器结构&#xff0c;指服务器中多个CPU对称工作&#xff0c;每个CPU访问内存地址所需时间相同。其主要特征是共享&#xff0c;包含对CPU&#xff0c;内存&#…