Canvas 保姆级教程(上):绘制篇 您所在的位置:网站首页 网页美工教案 Canvas 保姆级教程(上):绘制篇

Canvas 保姆级教程(上):绘制篇

2024-01-20 00:25| 来源: 网络整理| 查看: 265

元素可被用来通过 JavaScript( Canvas API 或 WebGL API )绘制图形及图形动画,也就是说 标签只是一个图形容器。Canvas 可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。

Canvas API 主要聚焦于 2D 图形。而 WebGL API 则用于绘制硬件加速的 2D 和 3D 图形。

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

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

只有同时通过 CSS 指定了 width 和 height,才会出现比例不一致,如果只定义 width: 400px,你会发现高度会自动变成 200px。

比如,尺寸比例不一致时会出现下面这个变形的圆:

#myCanvas { width: 400px; height: 150px; }

01.png

此时的画布尺寸依旧是初始值(w:300px, h:150px)。

getContext()

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

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

let ctx = canvas.getContext(contextType, contextAttributes?);

上下文类型(contextType)

2d:创建一个二维渲染上下文 webgl:创建一个三维渲染上下文(WebGL 版本 1) webgl2:创建一个三维渲染上下文(WebGL 版本 2) bitmaprenderer:创建一个只提供将 canvas 内容替换为指定 ImageBitmap 功能的 ImageBitmapRenderingContext。(safari 还不支持)

上下文属性(contextAttributes)

2d

alpha:布尔值,表明 canvas 包含一个 alpha 通道。如果设置为 false, 浏览器将认为 canvas 背景总是不透明的, 这样可以加速绘制透明的内容和图片.

webgl

alpha: 布尔值,表明 canvas 包含一个 alpha 通道。

antialias: 布尔值,表明是否开启抗锯齿。

depth: 布尔值,表明绘制缓冲区包含一个深度至少为 16 位的缓冲区。

failIfMajorPerformanceCaveat: 布尔值,表明在一个系统性能低的环境是否创建该上下文。

powerPreference: 指示浏览器在运行 WebGL 上下文时使用相应的 GPU 电源配置。 可能值如下:

default: 自动选择,默认值。

high-performance: 高性能模式。

low-power: 节能模式。

premultipliedAlpha: 布尔值,表明排版引擎将假设绘制缓冲区包含预混合 alpha 通道。

preserveDrawingBuffer: 布尔值,是否保存缓冲区,直到被清除或被使用者覆盖。

stencil: 布尔值,表明绘制缓冲区包含一个深度至少为 8 位的模版缓冲区。

一个简单的例子

01.png

在例子中绘制了两个有趣的长方形,其中的一个有着 alpha 透明度。

var canvas = document.getElementById("canvas"); // 得到DOM对象 if (canvas.getContext) { var ctx = canvas.getContext("2d"); // 得到渲染上下文 // 绘制第一个长方形 ctx.fillStyle = "rgb(200,0,0)"; ctx.fillRect(10, 10, 55, 50); // 绘制第二个长方形 ctx.fillStyle = "rgba(0, 0, 200, 0.5)"; ctx.fillRect(30, 30, 55, 50); } 坐标

在我们开始画图之前,我们先了解一下 canvas 的坐标体系(x,y)。canvas 是一个二维网格,原点 (0,0) 在左上角,所有元素的位置都相对于原点定位取正数。所以图中蓝色方形左上角的坐标为距离左边(X 轴)x 像素,距离上边(Y 轴)y 像素,它的坐标就是 (x,y):

01.png

绘制图形

只支持两种形式的图形绘制:矩形 和 路径(由一系列点连成的线段)。

矩形

提供了三种方法绘制矩形,矩形的左上角为(x,y),:

1. fillRect

绘制一个填充的矩形,填充色为当前的 fillStyle。

fillRect(x, y, width, height);

我们可以用 fillRect() 在开始绘制内容前,设置一个背景:

const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); ctx.fillRect(0, 0, canvas.width, canvas.height); 2. strokeRect

绘制一个矩形的边框,边框色为当前的 strokeStyle。

strokeRect(x, y, width, height); 3. clearRect

这个方法通过把像素设置为透明,以达到擦除一个矩形区域的效果。

clearRect(x, y, width, height);

请确保在调用 clearRect() 之后绘制新内容前调用 beginPath() 。

我们可以用 clearRect 清除整个画布:

const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); ctx.clearRect(0, 0, canvas.width, canvas.height); 路径

路径就像是画家手中的画笔,通过一笔一画的组合(路径列表)可以绘制出复杂而丰富的图形。

使用路径绘制图形的步骤:

第一步,创建路径起始点,beginPath()(清空路径列表)。 第二步,画出路径。 第三步(可选),闭合路径(路径生成),closePath()(绘制一条从当前点到开始点的直线来闭合图形)。 第四步,通过描边或填充路径区域来渲染图形,stroke() / fill()。 调用 fill(),所有没有闭合的形状都会自动闭合。 调用 stroke(),不会自动闭合。 移动笔触 moveTo moveTo(x, y); // 将笔触移动到坐标 (x,y) 上

这个方法完成的事情,可以想象成你在纸上作业,一支钢笔或者铅笔的笔尖从一个点到另一个点的移动过程。

我们可以用 moveTo 函数:

设置起点 绘制一些不连续的路径 线 lineTo lineTo(x, y);

这个方法是用来 绘制直线 的,绘制一条从当前位置到指定坐标 (x,y) 的直线。

当前位置由 之前路径的结束点 或者 moveTo() 决定。

我们结合 moveTo(),来绘制两个三角形,填充的和描边的。

// 填充三角形 ctx.beginPath(); ctx.moveTo(25, 25); ctx.lineTo(105, 25); ctx.lineTo(25, 105); ctx.fill(); // 自动闭合路径 // 描边三角形 ctx.beginPath(); ctx.moveTo(125, 25); ctx.lineTo(125, 105); ctx.lineTo(205, 25); ctx.closePath(); // 手动闭合路径 ctx.stroke();

01.png

圆弧

绘制圆弧或者圆,可以选择 arc() 或者 arcTo()。两个方法的绘制思路是不一样的,我们通常选择 arc(),因为更可控。

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

绘制的过程:

以 (x,y) 为圆心 以 radius 为半径 从 startAngle 开始到 endAngle 结束 按照给定的方向( anticlockwise,默认为 false 顺时针)来生成。

注意,startAngle 和 endAngle 的单位是弧度,而不是角度。 弧度 = ( Math.PI / 180 ) * 角度

我们来画个“月有阴晴月缺”:

for (i = 0; i < 12; i++) { ctx.beginPath(); let x = 25 + i * 50; let y = 25; let radius = 20; let startAngle = 0; let endAngle = Math.PI + (Math.PI * i) / 12; // 从半圆到全圆 ctx.arc(x, y, radius, startAngle, endAngle); ctx.fill(); }

01.png

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

绘制过程:

连接当前位置(蓝点)和控制点 1(x1,y1)得到直线 A 连接控制点 1(x1,y1)和控制点 2(x2,y2)得到直线 B 以 radius 做为半径 以直线 A 和直线 B 作为切线,画出两条切线之间的弧线路径

01.png

矩形 rect rect(x, y, width, height);

绘制一个左上角坐标为 (x,y),宽高为 width 以及 height 的矩形。

在该方法执行前,当前笔触自动重置回默认坐标,即 moveTo(0,0);该方法结束后的笔触停留在矩形左上角 (x,y)。

贝塞尔曲线

二次及三次贝塞尔曲线,一般用来绘制复杂有规律的图形。

因为贝塞尔曲线构建的图形很难想象,所以分享一个在线调试工具:

二次贝塞尔曲线调试器 三次贝塞尔曲线调试器

01.png

二次贝塞尔曲线

quadraticCurveTo(cp1x, cp1y, x, y)

绘制二次贝塞尔曲线,当前位置为开始点(蓝点),(cp1x,cp1y) 为一个控制点(红点),(x,y) 为结束点(蓝点)。

渲染对话气泡:

ctx.beginPath(); ctx.moveTo(75, 25); ctx.quadraticCurveTo(25, 25, 25, 62.5); ctx.quadraticCurveTo(25, 100, 50, 100); ctx.quadraticCurveTo(50, 120, 30, 125); ctx.quadraticCurveTo(60, 120, 65, 100); ctx.quadraticCurveTo(125, 100, 125, 62.5); ctx.quadraticCurveTo(125, 25, 75, 25); ctx.stroke();

01.png

三次贝塞尔曲线

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

绘制三次贝塞尔曲线,当前位置为开始点(蓝点,)(cp1x,cp1y) 为控制点 1(红点),(cp2x,cp2y) 为控制点 2(红点),(x,y) 为结束点(蓝点)。

绘制一颗心心:

ctx.beginPath(); ctx.moveTo(75, 40); ctx.bezierCurveTo(75, 37, 70, 25, 50, 25); ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5); ctx.bezierCurveTo(20, 80, 40, 102, 75, 120); ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5); ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25); ctx.bezierCurveTo(85, 25, 75, 37, 75, 40); ctx.fill();

01.png

Path2D

Path2D 可以用缓存或记录绘画命令,就像是把某一段路径抽成一个函数用于复用,这样做也简化了代码和提高了性能。

以上介绍的所有路径方法,在 Path2D 中都可以用,比如 moveTo, rect, arc 或 quadraticCurveTo 等。

Path2D()

返回一个新初始化的 Path2D 对象,可能是某一个路径,或者包含 SVG path 数据的字符串。

new Path2D(); // 空的Path对象 new Path2D(path); // 克隆Path对象 new Path2D(d); // 从SVG建立Path对象 创建和拷贝路径 var path1 = new Path2D(); path1.rect(10, 10, 100, 100); var path2 = new Path2D(path1); path2.moveTo(220, 60); path2.arc(170, 60, 50, 0, 2 * Math.PI); ctx.stroke(path2); SVG path

Path2D API 有另一个强大的功能,可以使用 SVG path data 来初始化 canvas 上的路径,这意味着可以将 SVG 搬上 canvas。

解读一下这一条 SVG path -- "M10 10 h 80 v 80 h -80 Z":

移动到点 (M10 10) 向右侧水平移动 80 个点 (h 80) 向下 80 个点 (v 80) 向左 80 个点 (h -80) 回到起始点 (z)

没错,最终绘制除了一个矩形:

var p = new Path2D("M10 10 h 80 v 80 h -80 Z"); ctx.fill(p); addPath Path2D.addPath(path[, transform])

addPath 可以将 path 结合起来(添加一条路径到当前路径),这很适用从几个元素中来创建对象。

给图形上色

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

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

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

色值

关于色值的内容比较简单,和我们在 CSS 中指定颜色也差不多:

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

在 canvas 中,渐变对象和图案对象是有自己套路的。

渐变对象

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

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.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置。 color:是一个有效的 CSS 颜色值。

一个简单的线性黑白渐变例子:

let linearGradient = ctx.createLinearGradient(0, 0, 150, 150); linearGradient.addColorStop(0, "#000"); linearGradient.addColorStop(1, "#fff");

01.png

一个径向渐变小彩球的例子:

// 创建渐变 var ball = ctx.createRadialGradient(45, 45, 10, 52, 50, 30); ball.addColorStop(0, "#A7D30C"); ball.addColorStop(0.9, "#019F62"); ball.addColorStop(1, "rgba(1,159,98,0)"); // 画图形 ctx.fillStyle = ball; ctx.fillRect(0, 0, 150, 150);

让起点稍微偏离终点,这样可以达到一种球状 3D 效果。在球体的边缘处,使用同色值透明度过渡会柔和一些。

01.png

图案对象

图案的应用跟渐变很类似的,创建出一个 pattern 之后,赋给 fillStyle 或 strokeStyle 属性即可。

createPattern(image, type); image:一个 Image 对象的引用,或者另一个 canvas 对象。 type:repeat,repeat-x,repeat-y 和 no-repeat。

注意: 与 drawImage 有点不同,你需要确认 image 对象已经装载完毕,否则图案可能效果不对的。

// 创建新 image 对象,用作图案 let img = new Image(); img.src = "someimage.png"; // 确认 image 对象加载完毕 img.onload = function () { // 创建图案 let ptrn = ctx.createPattern(img, "repeat"); ctx.fillStyle = ptrn; ctx.fillRect(0, 0, 150, 150); }; 线的样式 lineWidth

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

绘制十条宽度从 1.0 到 10.0 的线:

for (let i = 0; i < 10; i++) { ctx.beginPath(); ctx.lineWidth = i + 1; ctx.moveTo(5 + i * 14, 5); ctx.lineTo(5 + i * 14, 140); ctx.stroke(); }

01.png

因为线宽是指给定路径的中心到两边的粗细,所以在绘制过程中会在路径的两边各绘制线宽的一半。也正是因为这个规则,当我们的路径 Y 轴落在网格上时(像下图 2),两边的实际填充区域只占网格的一半,实际渲染中 canvas 会以实际笔触颜色一半色调的颜色来填充整个区域,所以,最左边的以及所有宽度为奇数的线并不能精确呈现。

所以在默认情况下,我们想要获得精确的线条,就要让奇数宽度的中心落在网格中心,偶数宽度的中心落在网格线上。在操作缩放时,这也是不可忽略的一点。

01.png

lineCap

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

01.png

lineJoin

决定了图形中两线段连接处所显示的样子。它的取值如下图所示,从下往上分别是 miter(默认)、bevel、round。其中 miter 的延伸效果会收到 miterLimit 的约束,也就是下面这个属性。

01.png

miterLimit

想象上图中的两线之间的夹角无限变小,那么外延交点也会无限变远。

miterLimit 属性就是用来设定外延交点与连接点的最大距离(默认为 10.0),如果交点距离大于此值,连接效果会变成了 bevel。

max miterLength(最长交点距离)= miterLimit * lineWidth miterLimit 默认为 10.0,这将会去除所有小于大约 11 度的斜接。 miterLimit 为 √2 ≈ 1.4142136 (四舍五入)时,将去除所有锐角的斜接,仅保留钝角或直角。 1.0 是合法的 miterLimit 值,但这会去除所有斜接。 小于 1.0 的值不是合法的 miterLimit 值。 虚线 setLineDash

在填充线时使用虚线模式。

setLineDash(segments); segments,一个数组,描述线段和间距。

提示:如果要切换回至实线模式,将 segments 设置为一个空数组即可。

一些常见的虚线模式:

function drawDashedLine(pattern) { ctx.beginPath(); ctx.setLineDash(pattern); ctx.moveTo(0, y); ctx.lineTo(300, y); ctx.stroke(); y += 20; } const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); let y = 15; drawDashedLine([]); // 实线 drawDashedLine([1, 1]); drawDashedLine([10, 10]); drawDashedLine([20, 5]); drawDashedLine([15, 3, 3, 3]); drawDashedLine([20, 3, 3, 3, 3, 3, 3, 3]); drawDashedLine([12, 3, 3]); // Equals [12, 3, 3, 12, 3, 3]

01.png

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();

01.gif

getLineDash()

getLineDash(),是获取当前线段样式的方法。返回值是一个描述线段和间隔的数组。如果数组元素的数量是奇数,数组元素会被复制并重复。

ctx.setLineDash([5, 15]); console.log(ctx.getLineDash()); // [5, 15] ctx.setLineDash([5, 15, 5]); // 数组元素的数量是奇数 console.log(ctx.getLineDash()); // [5, 15, 5, 5, 15, 5] 阴影 shadowOffsetX|Y 用来设定阴影在 X 和 Y 轴的延伸距离,它们是不受变换矩阵所影响的。 负值表示阴影会往上或左延伸, 正值则表示会往下或右延伸, 默认为 0。 shadowBlur 用于设定阴影的模糊程度,默认为 0。 shadowColor 用于设定阴影颜色效果,默认是全透明的黑色。 ctx.shadowOffsetX = 10; ctx.shadowOffsetY = 10; ctx.shadowBlur = 10; ctx.shadowColor = "rgba(23,23,23,0.5)"; ctx.fillRect(20, 20, 150, 150);

01.png

填充规则

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

ctx.fill(); ctx.fill(fillRule); ctx.fill(path, fillRule);

那么,什么是填充规则呢?

只要是路径填充,都有两种规则 -- nonzero 和 evenodd,无论是 SVG 中的路径填充,还是 Canvas 中的路径填充。

两组路径的差异体现在交叉点的处理上,也就是说如果只是一个三角形,是看不出区别的:

01.png

如果是两个发生重叠的三角形,产生了交叉点,也产生了差异:

02.png

evenodd

“奇偶规则” -- 计算交叉路径数量,计算结果是奇数,就填充;计算结果是偶数,就不填充。

比如我们在下图取一点 A,向任意方向发出一条射线(天蓝色),找到和射线发生交叉的路径,是路径 5 和路径 2,计算结果是偶数,所以不填充。

01.png

nonzero

“非零规则” -- 计算顺时针逆时针数量,计算结果不是 0,就填充;计算结果是 0,就不填充。

比如我们在下图取一点 B,向任意方向发出一条射线(天蓝色),找到那些和射线发生交叉的路径:

发生逆时针的偏转(路径 3),记 -1 发生顺时针偏转(路径 2),记 +1

计算结果是 0,所以不填充。

02.png

绘制文本 绘制

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

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

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

样式 font

描述当前字体样式,取值和 CSS 中的 font 属性一样,默认字体是 10px sans-serif。

ctx.font = "bold 48px serif"; textAlign

描述了文本水平方向的对齐方式,默认值是 start,取值如下:

ctx.textAlign = "left" || "right" || "center" || "start" || "end";

以下 x 和 y,是在 fillText/strokeText 的时候所给的。

"left",文本左对齐。 01.png

"right",文本右对齐。 01.png

"center",文本居中对齐。 01.png

"start",文本对齐界线开始的地方。

"end",文本对齐界线结束的地方。

direction 属性会对此属性产生影响:

如果 direction = ltr,则 left 和 start 的效果相同,right 和 end 的效果相同; 如果 direction = rtl,则 left 和 end 的效果相同,right 和 start 的效果相同 textBaseline

描述了当前文本基线,即文字垂直方向的对齐方式,默认值是 alphabetic,取值如下:

top,文本基线在文本块的顶部。 hanging,文本基线是悬挂基线。 middle,文本基线在文本块的中间。 alphabetic,文本基线是标准的字母基线。 ideographic,文字基线是表意字基线;如果字符本身超出了 alphabetic 基线,那么 ideograhpic 基线位置在字符本身的底部。 bottom,文本基线在文本块的底部。

01.png

direction

描述了当前文本方向。

ctx.direction = "ltr" || "rtl" || "inherit"; ltr,文本方向从左向右,如下图第一行。 rtl,文本方向从右向左,如下图第二行。

01.png

预测量文本宽度

measureText 方法将返回一个 TextMetrics 对象,只包含部分能体现文本特性的属性(宽度、所在像素):

ctx.measureText(text); var text = ctx.measureText("foo"); // TextMetrics object }

01.png

绘制图像

引入图像到 canvas 的步骤只需要两步:

获取图片资源 画上去 获取图片资源

canvas 支持的图片源类型:

HTMLImageElement:

new Image(): let img = new Image(); img.onload = function () { // drawImage to canvas }; img.src = "XXX.png"; : let img = document.getElementById("img"); img.onload = function () { // drawImage to canvas };

HTMLVideoElement: canvas 会绘制当前视频帧,我们可以搭配 video 事件使用,比如下面的例子:

let video = document.getElementById("video"); // 媒体的第一帧加载完成时,渲染第一帧 video.onloadeddata = function () {}; // 播放暂停时,渲染当前帧 video.onpause = function () {};

01 (1).gif

HTMLCanvasElement: 一个常用的应用就是将第二个 canvas 作为第一个 canvas 的缩略图:

let sourceCanvas = document.getElementById("source"); let thumbCanvas = document.getElementById("thumb"); thumbCtx.drawImage(source, 0, 0, 100, 50);

01.png

ImageBitmap:高性能的位图,可以低延迟地绘制,它运用 createImageBitmap() 工厂方法模式,它可以从多种源中生成。

drawImage()

获得图片对象后,我们通过 drawImage 方法将它渲染到 canvas 里。drawImage 参数有三种情况:三个参数,五个参数,九个参数。

基础用法(三个参数) drawImage(image, x, y); image:图片对象 x,y:在 canvas 中的起始坐标 缩放(五个参数) drawImage(image, x, y, width, height); width,height:图片对象在 canvas 中的绘制尺寸 切片(九个参数) drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

01.jpeg

状态的保存和恢复

save() 和 restore(),这两个方法是简单绘制和复杂绘制的一条分界线,可以帮我们省下不少的重复劳动。

save():保存画布的所有状态 restore():恢复保存的画布状态

画布的状态被保存在一个栈中,一个绘画状态包括:

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

变形是一种更强大的方法,它可以移动原点、对网格进行旋转和缩放。

移动 translate

移动 canvas 和它的原点到一个不同的位置。

translate(x, y);

x 是左右偏移量,y 是上下偏移量,如下图所示:

01.png

旋转 rotate

以原点为中心旋转 canvas。

rotate(angle);

angle 取正时,顺时针旋转;angle 取负时,逆时针旋转。以弧度为单位的值。

01.png

缩放 scale

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

scale(x, y); 默认值为 1, 为实际大小。 比 1 小,会缩小图形, 比 1 大,会放大图形。 如果是负数,相当于以 x 或 y 轴作为对称轴镜像反转 // 被拉伸的矩形 ctx.save(); ctx.scale(10, 3); ctx.fillRect(1, 10, 10, 10); ctx.restore(); // 以 y 轴为对称轴做镜像反转 ctx.scale(-1, 1); ctx.font = "48px serif"; ctx.fillText("荷包蛋卷", -193, 120);

01.png

变形矩阵 transform

对变形矩阵直接修改。

基础用法

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

重置矩阵 重置当前变形为单位矩阵,等同于 ctx.setTransform(1, 0, 0, 1, 0, 0);

resetTransform();

重置并修改矩阵 将当前的变形矩阵重置为单位矩阵,然后用相同的参数调用 transform 方法。

setTransform(a, b, c, d, e, f); 组合

globalCompositeOperation 提供了 12 种合成图形的方式:

名称描述展示source-over默认设置,直接在原有内容上绘制新内容01.pngsource-in新内容只在新内容和原有内容重叠的区域绘制,其他都是透明的。02.pngsource-out新内容在新内容和原有内容不重叠的区域绘制,其他都是透明的。03.pngsource-atop新内容只在新内容和原有内容重叠的区域绘制, 其他都是保持原有内容的。04.pngdestination-over在原有内容下面绘制性新内容05.pngdestination-in原有内容只保留新内容和原有内容重叠的区域,新内容不显示06.pngdestination-out原有内容只保留新内容和原有内容不重叠的区域,新内容不显示01.pngdestination-atop原有内容只保留新内容和原有内容重叠的区域绘制,新内容全部保留02.pnglighter颜色值相加03.pngcopy只显示新图形。04.pngxor重叠区域是透明的05.pngmultiply像素相乘(更黑暗)06.pngscreen像素被倒转,相乘,再倒转(更明亮)07.pngoverlaymultiply 和 screen 的结合,原本暗的地方更暗,原本亮的地方更亮。08.pngdarken保留两个图层中最暗的像素。09.pnglighten保留两个图层中最亮的像素。10.pngcolor-dodge将底层除以顶层的反置。01.pngcolor-burn将反置的底层除以顶层,然后将结果反过来。02.pnghard-lightmultiply 和 screen 的结合,但上下图层互换了。03.pngsoft-light用顶层减去底层或者相反来得到一个正值。05.pngdifference一个柔和版本的 hard-light。纯黑或纯白不会导致纯黑或纯白。06.pngexclusion和 difference 相似,但对比度较低。07.pnghue保留了底层的亮度(luma)和色度(chroma),同时采用了顶层的色调(hue)。08.pngsaturation保留底层的亮度(luma)和色调(hue),同时采用顶层的色度(chroma)。09.pngcolor保留了底层的亮度(luma),同时采用了顶层的色调(hue)和色度(chroma)。10.pngluminosity保持底层的色调(hue)和色度(chroma),同时采用顶层的亮度(luma)。11.png

其中,destination-in的遮盖方式,可以用于实现抠图效果(只显示涂抹区域)。

裁剪

clip() 方法其实是绘制图形(stroke(),fill())的第三个进阶方法,clip() 会将当前正在构建的路径转换为当前的裁剪路径,所有在路径以外的部分都会被隐藏。

默认情况下,canvas 有一个与它自身一样大的裁切路径(也就是没有裁切效果)。

我们来画一个星空,用一个圆形的裁切路径来限制随机星星的绘制区域:

function draw() { var ctx = document.getElementById("canvas").getContext("2d"); // 黑色背景 ctx.fillRect(0, 0, 600, 600); ctx.translate(300, 300); // 圆形裁剪区域 ctx.beginPath(); ctx.arc(0, 0, 250, 0, Math.PI * 2, true); ctx.clip(); // 渐变蓝色背景 var lingrad = ctx.createLinearGradient(0, 0, 600, 600); lingrad.addColorStop(0, "#232256"); lingrad.addColorStop(1, "#143778"); ctx.fillStyle = lingrad; ctx.fillRect(-300, -300, 600, 600); // 画星星✨ for (var j = 1; j < 100; j++) { ctx.save(); ctx.fillStyle = "#fff"; ctx.translate( 250 - Math.floor(Math.random() * 500), 250 - Math.floor(Math.random() * 500) ); drawStar(ctx, Math.floor(Math.random() * 4) + 2); ctx.restore(); } } function drawStar(ctx, r) { ctx.save(); ctx.beginPath(); ctx.moveTo(r, 0); for (var i = 0; i < 9; i++) { ctx.rotate(Math.PI / 5); if (i % 2 == 0) { ctx.lineTo((r / 0.525731) * 0.200811, 0); } else { ctx.lineTo(r, 0); } } ctx.closePath(); ctx.fill(); ctx.restore(); }

01.png

HTML5 系列 了解 HTML5 增强型表单 音频和视频 Canvas 保姆级教程(上):绘制篇 Canvas 保姆级教程(下):动画篇 终于会用 SVG 画图了 地理定位 Geolocation 拖放操作 了解 Web Worker 了解 Web Storage 了解 WebSocket


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有