当前位置:网站首页 > 深度学习 > 正文

canvas学习笔记

一,canvas的开发概念建立

最开始的时候,浏览器只是提供了一个canvas的标签以及它对应的api.来帮助我们在浏览器创建一个画布,以便我们在该画布上绘制想要的各种图案.

这里需要对画板有个基础的概念.canvas和小朋友的玩具画板一样,如下图:

在这里插入图片描述

本篇文章主要讲canvas原生的api,让开发者对canvas的开发有个直观的印象.

1.1,面向对象的编程方式

上文说到,canvas是一个画板,那么我们要在这个画板上进行绘画,首先第一步要做的就是拿到这个画板.

<canvas id="canvas" width="900" height="900"></canvas> var canvasEl = document.getElementById("canvas"); var ctx = canvasEl.getContext("2d");//获取到这个画布对象 

在我们平时的业务开发中,往往都是面向过程的开发方式.而canvas实际上是个画板对象,使用canvas更多的时候,我们是使用的面相对象的思维来开发.

1.2,绘图的方式

在画板上,画完的东西是不可改的,只能覆盖或者重新画;也无状态,canvas 并不知道你画的是啥,也不知道你鼠标点的是哪个物体,要是想稍微修改下画布上面物体的大小或者让物体动起来,那一定是要清除(整个或局部)画布再重新绘制的。 当重新绘制的速度足够快,那么人眼看起来,画板上的内容就是被修改了或者变成了动画.

具体如下图所示:

在这里插入图片描述

二,canvas画板

<canvas id="canvas" width="150" height="150"></canvas> 
2.1,画板对象的默认宽高

<canvas> 标签只有两个属性—— widthheight,当没有设置宽度和高度的时候,<canvas> 会初始化宽度为 300px 和高度为 150px

需要注意的是, 通过 CSS 也可以定义 canvas 的尺寸,但此元素尺寸非彼画布尺寸,在绘制时图像会伸缩以适应它的画布尺寸;如果元素尺寸和画布尺寸比例不一样,绘制出来的图像是扭曲的。

2.2,画板对象的属性设置

我们可以在画板对象上设置样式:

 var canvasEl = document.getElementById("canvas"); canvasEl.style.background = 'grey' 
2.3,画板对象的坐标体系

canvas的坐标和我们平时的坐标系不一样,左上角是坐标原点(0,0)

在这里插入图片描述

2.4,线条默认宽度和颜色

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

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

在这里插入图片描述

2.5,渲染上下文getContext()

<canvas> 元素创造了一个固定大小的画布,它公开了一个或多个渲染上下文,其可以用来绘制和处理要展示的内容.

canvas 起初是空白的,脚本首先需要找到渲染上下文,然后在它的上面绘制.getContext(),这个方法是用来获得渲染上下文和它的绘画功能.

我们这篇文章只讲2d绘图功能.

<canvas id="canvas" width="900" height="900"></canvas> var canvasEl = document.getElementById("canvas"); var ctx = canvasEl.getContext("2d");//获取到这个画布对象 

这个ctx对象上,就拥有一些属性和方法,利用它,我们能够在这个画板上进行绘图.

2.6,绘图状态和路径

绘图状态和路径是canvas学习过程中最最重要的两个概念,理解了这两个概念,canvas就变得很简单了.

这里只是简单提及,具体还是要在下文细细理解.

我们使用canvas绘制一幅图案通常需要做以下几件事:

1,拿到画板 2,定义好画板和画笔的状态(变形啦,裁剪路径啦,画笔的颜色宽度啦之类的属性)(没有定义则是默认状态) 3,定义好裁剪路径(没有定义则是全画幅) 4,定义好路径 5,基于以上几点,利用stroke,fill,drawImage等api在画板上进行绘制 

这里的第二步,就是我们的绘图状态,它通常需要结合ctx.save()和ctx.restore()来使用,以避免绘图状态之间的干扰.

这里的第四步,就是我们定义的路径,路径的定义只是告诉canvas这些路径,而并没有进行绘制.路径则需要经常使用ctx.beginPath()来重新开启一次路径的绘制.

三,绘图状态的定义与存储

当我们在画画前,要做的事情是不是选择一款合适的画笔(当我们没有选择时,canvas会提供默认的)?canvas对象有一系列的属性,来定义该画笔的形态.

3.1,画笔的定义(绘图状态)

绘图状态是canvas中非常重要的一个概念,每次画图,都是基于当前的绘图状态,类比于ps中的画笔加图层.

这就是一般文章中说的:图形上下文对象(CanvasRenderingContext2D)的当前属性值,具体如下:

  • 属性:
    • 描边/填充样式:strokeStyle, fillStyle, globalAlpha
    • 线的样式:lineWidth, lineCap, lineJoin, miterLimit, lineDashOffset
    • 阴影:shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor,
    • 字体样式:font, textAlign, textBaseline, direction
    • 平滑质量:imageSmoothingEnabled
    • 合成属性:globalCompositeOperation
  • 当前变形
  • 当前裁剪路径

比如当我们要画一个矩形时,就可以先定义好状态,再进行绘制:

在这里插入图片描述

3.2,画笔的重置

当我们画完一个路径,就需要重置路径,否则会前后绘制的图案之间的画笔属性会相互干扰,具体怎么触发干扰就不展开,这里只讲避免干扰的方法.那就是养成绘制图形前重置路径的习惯.一般我们会声明开启一条新的路径来重置路径.

ctx.beginPath()//告诉canvas开始一条新的路径,这时候可以设定新的画笔属性 

这样一来,相当于就是告诉canvas你已经换了一只画笔(当然,如果没有重新设置画笔属性,那么之前的画笔属性将被沿用)

在这里插入图片描述

3,3,绘图状态的存储与复用

画过彩绘的都知道,我们画一幅色彩鲜艳的画作,不可能只使用一支笔.在作画过程中,我们可能使用很多支笔,但是我们总不能频繁地定义画笔状态吧?

为了方便起见,canvas提供了一个栈结构来存储画笔的状态.

然后使用如下两个api进行画笔状态的存储与复用:

ctx.save()//存储画笔状态 ctx.restore()//将栈中的画笔状态弹出使用 

值得注意的是,这里弹出的画笔状态就是3.1列表中的画笔属性集合.

在这里插入图片描述

其实我感觉这样还是不够方便因为画笔状态在栈中,画笔多了,管理很乱(弹出后不再次入栈就会丢失),如果是我设计的话,应该是能让用户自定义画笔预设,不同的画笔预设起自定义的名字,用一个对象Object去存储,用户想要什么画笔,就用Object[‘key’]去取得对应的画笔状态会更方便些.

四,基本图形绘制

有了上文的了解,就能够开始绘制一些基本的图形.

4.1,线条的绘制
4.1.1,单条直线的绘制

想象下,当我们在一个画板上绘制一条线,是不是需要先将笔落点,然后绘制一条直线.于是需要用到这两个方法:

ctx.moveTo(x1, y1):起点坐标 (x, y),也就是将笔尖移动到这一点 ctx.lineTo(x2, y2):下一个点的坐标 (x, y) 

需要注意的是,在canvas的绘画体系中,执行了上述方法,只是告诉了canvas我们要画的东西,而实际上还需要执行:

ctx.stroke():将所有坐标用一条线连起来 

来将刚刚描述好的图案绘制到画板上.

于是绘制线条的代码和图案如下:

在这里插入图片描述

4.1.2,多条直线的绘制

多条直线其实和单条直线差不多,无非就是每画一条新的线的时候,需要先把画笔抬起来,然后重新落点,也就是ctx.moveTo()方法.

在这里插入图片描述

4.1.3,折线的绘制

折线的绘制,其实就是一次落笔,多次移动,所以是一次ctx.moveTo,多次lineTo.

在这里插入图片描述

4.2,矩形的绘制
4.2.1.矩形的生成

canvas提供了一种矩形的绘制方法

ctx.rect(x, y, width, height)//参数为左上角的xy值和矩形的长宽 

当然,这种方法只是告诉了canvas需要画怎样的图形,还没有开始画,如果想要在画板上画出来,还是需要结合ctx.stroke()来画线,或者结合ctx.fill()来填充

既然是描边和填充,肯定需要预先定义画笔状态(strokeStyle和fillStyle),于是代码和效果如下:

在这里插入图片描述

canvas还提供了直接绘制描边矩形和填充矩形的方法:

ctx.strokeRect(x,y,with,height); ctx.fillRect(x,y,width,height); 

这种写法和上文rect绘制的矩形效果一样:

 ctx.beginPath() ctx.lineWidth = 10 ctx.strokeStyle = 'pink' ctx.fillStyle = 'blue' ctx.strokeRect(20,20,100,100) ctx.fillRect(20,20,100,100) 
4.2.2,矩形的清除

矩形的清除,其实就是绘制一个canvas背景色的矩形,看起来就是擦除了一块矩形.

clearRect(x, y, width, height)//擦除一个矩形区域 clearRect(0, 0, canvas.width, canvas.height)//清除画布 
4.3,多边形的绘制

多边形的绘制,其实就是折线的绘制,但是要处理下折线的闭合,于是相较于折线的绘制,在多边形的绘制上,canvas提供了一个新的api来完成折线的闭合.

它的作用就是将结束点和开始点连接起来,形成一个闭环.

ctx.closePath() 

在这里插入图片描述

4.4,圆形的绘制

canvas提供了圆形的绘制方法,因为圆形是闭合的几何体,所以必须调用ctx.closePath().

arc(x, y, r, sAngle, eAngle,counterclockwise)//xy是圆心坐标,r是半径,sAngle是开始角度,eAngle是结束角度,counterclockwise是方向,true是逆时针,false是顺时针,默认false 
4.4.1,圆形的绘制

圆形的绘制如下图:

在这里插入图片描述

又因为我们使用了闭合路径,所以我们可以只绘制一部分圆:

在这里插入图片描述

4.4.2,弧线的绘制

通过上文对closePath的理解,想画一段圆弧的话,只要不执行这个api,让圆形不闭合就行.

在这里插入图片描述

当然canvas还提供了一个专门绘制圆弧的api

arcTo(cx, cy, x2, y2, radius)//(切线交点x,切线交点y,结束点x,结束点y,半径) 

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

在这里插入图片描述

4.5,贝塞尔曲线

贝塞尔曲线一般用来绘制有规律的复杂图案,根据控制点的多少,可以分为二次贝塞尔曲线和三次贝塞尔曲线.贝塞尔曲线,实际上就是控制点连接线为切点形成的曲线.

4.5.1,二次贝塞尔曲线

需要先确定一个起始点,然后利用quadraticCurveTo进行绘制,对于起点的设置,就是画图中的笔尖落点moveTo.

这里有个在线调试贝塞尔曲线的网址:二次贝塞尔曲线

quadraticCurveTo(cp1x, cp1y, x, y)//(控制点的x坐标,控制点的y坐标,结束点的x坐标,结束点的y坐标) 

在这里插入图片描述

4.5.2,三次贝塞尔曲线

三次贝塞尔曲线,实际上就是具备两个控制点的曲线,于是就是在二次贝塞尔曲线的基础上增加一个控制点.

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)//控制点1的坐标,控制点2的坐标,结束点的坐标 

这里有个在线调试三次贝塞尔曲线的网址:三次贝塞尔曲线

在这里插入图片描述

4.6,path2D对象存储和复用路径

Canvas 2D API 的接口 Path2D 用来声明路径,此路径稍后会被CanvasRenderingContext2D对象使用.CanvasRenderingContext2D 接口的路径方法也存在于 Path2D 这个接口中,允许你在 canvas 中根据需要创建可以保留并重用的路径。

在上文的绘图中,我们一直都是利用var ctx = canvasEl.getContext(“2d”),在整个画布的绘图上下文ctx上不断地绘制路径,那如果我们想重用某段路径,就需要一个对象来保存该路径.这就是path2D的最大作用.

4.6.1,path2D的创建和复用路径

如下图,通过var path1=new Path2D()创建了一条新的路径,并且通过path1这个变量完成对它的存储.

而后,创建一个新路径pathOther,可以利用addPath命令添加到原有的路径path1上.这样就构建出一条新的路径.

当我们想要复用之前的路径的时候,就可以利用path2=new Path2D(path1)来基于path1创建新的路径,这样就复用了path1.

值得注意的是,我们使用ctx.stroke(path2)的时候,绘制的只是一条路径path2,那也就是说它的颜色等样式只能通过ctx设置一次,无法做到其中的每条子路径不同的样式.

在这里插入图片描述

4.6.2,path2D的SVG解析

Path2D API 有另一个强大的功能,可以使用 SVG path data 来初始化 canvas 上的路径,于是我们可以用它来解析SVG.

在这里插入图片描述

4.7,判断点是否在路径上

canvas提供了ctx.isPointInStroke(x,y)来判断一个点是否在路径上的方法,在则返回true,否则返回false.

ctx.isPointInStroke(100,0) 
 ctx.beginPath() ctx.moveTo(50,50) ctx.lineTo(200,50) ctx.stroke() console.log("判断是否在路径上",ctx.isPointInStroke(100,50))//true 
4.8,判断点是否在填充区域内

canvas提供了ctx.isPointInPath(x,y)来判断一个点是否在填充范围内的方法,在则返回true,否则返回false.

ctx.isPointInPath(x,y) 
 ctx.beginPath() ctx.rect(50,50,200,200) ctx.stroke() console.log("判断是否在填充范围内",ctx.isPointInPath(100,100))//true 

五,颜色的填充

我们想为图形添加样式和颜色,可以用上这两个属性:

  • fillStyle:设置图形的填充颜色。
  • strokeStyle:设置图形轮廓的颜色。

它俩的默认值都是黑色(#000000),接受色值、渐变对象和图案对象.

5.1,色值的设置

canvas的颜色支持这几种:

// 色值 ctx.fillStyle = "orange"; ctx.fillStyle = "#FFA500"; ctx.fillStyle = "rgb(255,165,0)"; ctx.fillStyle = "rgba(255,165,0,0.2)"; // 透明度 0.2 

在这里插入图片描述

5.2,填充对象

填充对象有两种,一种是径向的,一种是线性的.

也是用 线性 或者 径向 的两种渐变形式来新建一个 canvasGradient 对象,并且赋给图形的 fillStylestrokeStyle 属性。

createLinearGradient(x1, y1, x2, y2):渐变的起点 (x1,y1) ,终点 (x2,y2) createRadialGradient(x1, y1, r1, x2, y2, r2):定义了两个圆 - 一个以 (x1,y1) 为原点,半径为 r1 的圆 - 一个以 (x2,y2) 为原点,半径为 r2 的圆 

创建出 canvasGradient 对象后,用 addColorStop 方法给它上色:

gradient.addColorStop(position, color);//position是0到1.0之间的数字.表示所在的位置比例,color是需要有效的css值 

先看线性的渐变:

在这里插入图片描述

再看径向的渐变:
在这里插入图片描述

5.3,填充图像

对于图片的填充,第一步,肯定是拿到图片,然后createPattern创建图片填充

ctx.fillStyle = ctx.createPattern(img, 'repeat') 

在这里插入图片描述

六,线条的样式

6.1,lineWidth线条宽度

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

在这里插入图片描述

6.2,lineCap线帽

决定了线段端点显示的样子。它的取值如下图所示,从上到下分别是 butt(默认)、roundsquare

在这里插入图片描述

6.3,虚线
6.3.1,虚线的绘制
ctx.setLineDash(segments);//segments是个数组,用来描述线段和间距,应该是偶数 

在这里插入图片描述

6.3.2,虚线的偏移量lineDashOffset

lineDashOffset 可以设置虚线的偏移量,默认值为 0.0。

ctx.lineDashOffset = value; 

利用这个偏移量,我们可以让虚线“动起来”,俗称“蚂蚁线”。

let offset = 0; function draw() { 
    ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.setLineDash([4, 4, 12, 4]); ctx.lineDashOffset = offset; ctx.strokeRect(20, 20, 150, 150); } function march() { 
    offset++; if (offset > 24) { 
    offset = 0; } draw(); setTimeout(march, 20); } march(); 

七,阴影

shadowOffsetX|Y:用来设定阴影在 X 和 Y 轴的延伸距离,它们是不受变换矩阵所影响的。 shadowBlur:用于设定阴影的模糊程度,默认为 0。 shadowColor:用于设定阴影颜色效果,默认是全透明的黑色 

在这里插入图片描述

八,填充规则

填充规则有两种,一种是默认的非零填充和奇偶环绕填充.

在 canvas 中,可以通过给 fill(或者 clipisPointinPath )传参指定填充规则,默认为 nonzero

ctx.fill(); ctx.fill(fillRule); ctx.fill(path, fillRule); 
8.1,非零填充规则

因为每次在画板上绘制路径肯定是有顺序的,所以必然是按照顺时针绘制或者逆时针绘制.

于是非零填充其实就是,从任意一点绘制射线,如果与顺时针路径相交,则计数器+1,如果与逆时针的路径相交,则计数器-1,当该射线的所有交点计数器和为0时不填充,否则填充.

如下图:

img A:先遇到顺时针的路径,计数器+1,然后遇到一条逆时针的路径,计数器-1,遇到逆时针-1,又遇到顺时针的+1,最后得0,于是A点不填充.

在这里插入图片描述

8.2,奇偶环绕填充

奇偶环绕规则更加简单,和上文一样绘制一样的图案,任意一点发射射线,与路径交点总数为偶数则不填充,为奇数则填充.

在这里插入图片描述

九,文本的绘制

canvas提供了两种绘制文本的方法:

fillText(text, x, y [, maxWidth])//在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。 strokeText(text, x, y [, maxWidth])//在指定的 (x,y) 位置绘制文本边框,绘制的最大宽度是可选的。 

具体的属性设置有:

属性名称 属性值 使用案例
font 默认字体是 10px sans-serif ctx.font = “bold 48px serif”;
textAlign “left” / “right” /“center” / “start” / “end” ctx.textAlign = “left”
textBaseline top,文本基线在文本块的顶部。
middle,文本基线在文本块的中间。
alphabetic,文本基线是标准的字母基线。
ideographic,文字基线是表意字基线;如果字符本身超出了 alphabetic 基线,那么 ideograhpic 基线位置在字符本身的底部。
hanging,文本基线是悬挂基线。
bottom,文本基线在文本块的底部。
ctx.textBaseline=‘top’
direction “ltr” / “rtl” /“inherit” ctx.direction=rtl
预测量文本宽度 measureText 方法将返回一个 TextMetrics对象,只包含部分能体现文本特性的属性(宽度、所在像素): var text = ctx.measureText(“foo”); // TextMetrics object

十,绘制图像

10.1,canvas支持获取的图片类型HTMLImageElement

主要有两种,一种是js动态创建的Image对象,另外一种是html中的image标签.

10.1.1,动态创建的Image图像

动态创建的图像,当我们想要绘制到canvas上的时候,需要先确保图片已经加载完全,所以需要放置在img.onload的回调函数中:

 let img = new Image(); img.src = imgUrl;//引入的图片地址('../test.png') img.onload = function () { 
    // drawImage to canvas }; 
10.1.2,HTML中的image标签

这个因为页面已经存在,就可以直接获取使用了:

<img id="img" src="XXX.png" /> <script> let img = document.getElementById("img"); img.onload = function () { 
    // drawImage to canvas }; </script> 
10.2,drawImage()绘制图像到canvas中

获得图片对象后,我们通过 drawImage 方法将它渲染到 canvas 里。这个api有三种使用方法,主要是参数数量的不同.

10.2.1,drawImage(image, x, y)直接绘制图像
drawImage(image, x, y);//image就是上文获取到的图片对象,xy是开始绘制的左上角坐标 

在这里插入图片描述

10.2.2,drawImage(image, x, y, width, height);对图像进行缩放

五个参数的时候,可以指定图片的宽高,这样一来,就可以进行图片的缩放.

在这里插入图片描述

10.2.3,drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);对图像进行截取并缩放
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); //sx, sy,源目标的左上角左上角开始点 //想要从开始点截取指定长宽的范围 //dx, dy绘制图像的左上角开始点 //图片的指定宽高,实现缩放 

也就是这个九个参数实现的事从原图像中抠出一部分内容,缩放一下,绘制在canvas中.

在这里插入图片描述

10.3,图片的裁剪

canva提供了一个裁剪图片的api:clip() 会将当前正在构建的路径转换为当前的裁剪路径,所有在路径以外的部分都会被隐藏。

于是乎,使用clip方法一般要经历以下三步:

1,绘制基本图形 2,使用clip()方法将该图形转化为裁剪路径 3,绘制图片 

在这里插入图片描述

也就是说,原来的图片照常绘制,只不过只显示裁剪路径内容罢了.

当我们进行裁剪的时候,需要注意一点,因为裁剪范围是属于绘图状态的,所以为了避免后续的绘图被这个裁剪路径所裁剪(裁剪会作用于当前裁剪后的绘图状态下的所有绘制的图片,也就是裁剪后再绘图都是被裁剪的),就可以在裁剪前ctx.save()保存下未裁剪前的绘图状态,然后在执行裁剪后,确认不再需要裁剪了,就ctx.restore()来恢复未裁剪的绘图状态.

十一,变形操作

canvas的移动、旋转、变形斗士针对画布进行操作的,对于canvas而言,这些变形定义的是状态,所以应该在fill、stroke、drawImage等操作之前完成.

另外值得注意的是:

当你使用Canvas的变形操作(如ctx.rotate)时,它只会影响之后绘制的图形,而不会影响之前已经绘制的图形。

Canvas绘图上下文(ctx)维护了一个绘图状态栈(graphics state stack)(这个上文3.1中的绘图状态提到过),其中包含了当前的变换矩阵(transformation matrix)。当你调用变形操作时,比如ctx.rotate(angle),它会修改当前的变换矩阵,然后之后的绘制操作都会基于这个新的变换矩阵进行变换。

但是,已经绘制在画布上的图形是不可更改的,它们已经被固定在画布上。变形操作只会影响之后的绘制操作,不会改变已经绘制的图形,如果要都变形,则需要在执行变形状态后重新绘制.

11.1,移动translate
ctx.fillRect(0, 0, 100, 100); ctx.translate(50,100) 

就是将该矩形移动(50,100)

11.2,旋转 rotate

旋转依然是以原点为圆心进行的旋转操作.

rotate(弧度);//正值以x轴顺时针,负值逆时针,弧度=角度*PI/180 

在这里插入图片描述

11.3,缩放scale

对形状,位图进行缩小或者放大,即增减图形在 canvas 中的像素数目。

scale(x, y);//默认是1,比1大则放大,比1小则缩小,如果是负数,相当于以 x 或 y 轴作为对称轴镜像反转 

在这里插入图片描述

11.4,变形矩阵transform

就是实使用这个api对图形一次性进行多角度更改:

transform(a, b, c, d, e, f); a:水平方向的缩放 b:竖直方向的倾斜偏移 c:水平方向的倾斜偏移 d:竖直方向的缩放 e:水平方向的移动 f:竖直方向的移动 

在这里插入图片描述

11.5,将变换的中心点移动到矩形中间

上文的变化基点都是以坐标原点为基准,我们可以使用translate方法将该绘图状态的坐标原点移动到canvas上任意我们想要的位置上.然后基于这个位置进行绘图和变化,这样就能实现变化中心点的指定.

在这里插入图片描述

如上图,我们就是将当前绘图状态的坐标原点切换到原先坐标系的(200,200)位置.然后在该绘图状态下的坐标系下绘制图案,注意到这时图案的左上角我用了负数的坐标,以此来保证该坐标原点在图案的中心,于是就可以基于该点做变换了.

十二,像素的操作与滤镜

12.1,图片像素数据的获取

canvas提供了图片像素的获取api,它可以帮助我们获取图片中指定区域的像素信息.

 let imageData=ctx.getImageData(x,y,width,height)//xy是定位左上角,width和height是取对应的区域范围 

使用的示例如下:

 let img = new Image(); img.src = imgUrl;//引入的图片地址('../test.png') img.onload = function () { // 绘制图像 ctx.drawImage(img,0, 0,300,300); let imageData=ctx.getImageData(10,10,20,20) console.log("图片像素数据",imageData.data) }; 

获取到的data是个数组,里面的内容大致如下:

[r1,g1,b1,a1,r2,g2,b2,a2,r3,g3,b3,a3……] 

可以看到,每四个为一组,表示着一个像素点的rgba值.这样,我们就能拿到指定区域的像素信息了.

但是我们一般说的图片信息就是imageData而不是imageData.data.

12.2,使用像素数据绘制图片

同样的,我们也可以直接利用一系列像素数据在canvas上绘制出图案.

ctx.putImageData(imageData,x,y)//参数依次为像素数据组,开始绘制的左上角坐标 

在这里插入图片描述

到目前为止,我们已经能获取图片指定位置的像素,要是将这些像素再处理一下,然后再进行绘制,不就能够实现图片的滤镜功能了嘛.

12.3,反相滤镜效果

反相操作的算法是:红绿蓝三个通道的值取相反值,即255-原值

 let img = new Image(); img.src = imgUrl;//引入的图片地址('../test.png') img.onload = function () { 
    ctx.drawImage(img,0, 0,300,300);//绘制图像 let imageData=ctx.getImageData(0,0,300,300)//获取像素信息 let resultData=invert(imageData)//像素处理:反相 ctx.putImageData(imageData,300,0)//绘制反相后的图片 }; function invert(imageData){ 
    //反相操作的算法是:红绿蓝三个通道的值取相反值,即255-原值 let data=imageData.data for(let i=0;i<data.length;i+=4){ 
    data[i]=255-data[i] data[i+1]=255-data[i+1] data[i+2]=255-data[i+2] } return imageData } 

值得注意的是imageData里面的属性都是只读的,不能直接更改和赋值。所以这里我直接在invert函数中传入imageData,不修改imageData的属性data(引用地址),而修改data的值(它不是只读了).

实现的效果:

在这里插入图片描述

12.4,黑白效果滤镜

黑白滤镜又叫做灰度图,是指将彩色照片转化成黑白的照片.实现的算法是:将红绿蓝的变成取红、绿、蓝三个通道的平均值.但是通常为了达到更好的效果,一般会给每个通道一个加权系数:

 let img = new Image(); img.src = imgUrl;//引入的图片地址('../test.png') img.onload = function () { 
    ctx.drawImage(img,0, 0,300,300);//绘制图像 let imageData=ctx.getImageData(0,0,300,300)//获取像素信息 let resultData=average(imageData)//像素处理:反相 ctx.putImageData(imageData,300,0)//绘制反相后的图片 }; //黑白滤镜:红绿蓝三个通道取三通道的平均值 function average(imageData){ 
    let data=imageData.data for(let i=0;i<data.length;i+=4){ 
    let averageValue=data[i]*0.3+data[i+1]*0.6+data[i+2]*0.1 data[i]=data[i+1]=data[i+2]=averageValue } return imageData } 

实现的效果:

在这里插入图片描述

12.5,对区域进行像素操作

上文中,我们是通过在canvas中先绘制一个图片,利用图片得到可操作的像素主体,而有的时候,我们希望能够直接创建一块区域,能够让我们进行像素操作.

ctx.createImageData(width,height)//参数表示创建的可操作像素区域大小(不在canvas上,可以类比于游离的dom元素) 
 let imageData=ctx.createImageData(30,30)//创建可编辑像素区域 ctx.putImageData(imageData,20,20) 
到此这篇canvas学习笔记的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • Linux学习笔记1---Windows上运行Linux2024-12-01 09:18:05
  • “黑客”入门学习之“单机游戏外挂原理与实现”_cheatengine什么原理2024-12-01 09:18:05
  • 海思3559AV100 MPP学习2--MPP平台初识2024-12-01 09:18:05
  • STATA学习笔记:缺漏值的处理2024-12-01 09:18:05
  • 深度学习的进展_深度学习的进展2024-12-01 09:18:05
  • webpack5学习与实战-(十二)-webpack模块与解析原理2024-12-01 09:18:05
  • webpack5学习与实战-(八)-配置打包后的文件名2024-12-01 09:18:05
  • webpack5学习与实战-(七)-代码分离2024-12-01 09:18:05
  • webpack5学习与实战-(五)-直接加载资源2024-12-01 09:18:05
  • webpack5学习与实战-(三)-引入其他资源模块2024-12-01 09:18:05
  • 全屏图片