HTML5 표준이 나온 지 오래됐지만 현재는 Canvas가 많이 사용되지 않는 것 같습니다. 매우 중요한 이유는 Canvas의 표준이 완전히 결정되지 않았으며 프로덕션 환경에서 대규모로 사용하기에 적합하지 않다는 것입니다. 그러나 Canvas의 장점도 매우 분명합니다. 예를 들어 많은 수의 요소가 포함된 차트를 그릴 때 SVG는 성능 문제로 인해 작업을 수행하지 못하는 경우가 많습니다. 효과는 비교적 눈부셨지만 각 아바타가 DOM이고 애니메이션이 CSS3로 제어되어 성능이 매우 낮기 때문에 실패했습니다. 또한, 하드웨어 성능이 향상되면서 점차적으로 웹 페이지에서도 동영상 스크린샷, 이미지 처리 등의 기능이 구현될 수 있게 되었습니다. 대부분의 웹사이트에서는 Flash를 사용하지만, Mac 컴퓨터에서는 Flash의 성능이 높지 않기 때문에 추가적인 학습이 필요합니다. 지식. . Canvas는 그리기에 JavaScript를 직접 사용하고 Mac에 친화적이므로 Flash의 후속 제품이라고 볼 수 있습니다.
캔버스 사용
그런데 캔버스란 정확히 무엇인가요?
캔버스(Canvas)는 영어로 '캔버스'를 뜻하는데 여기서 언급한 캔버스는 개발자가 일련의 그래픽을 그릴 수 있는 HTML5의 새로운 요소다. Canvas가 HTML 파일에 작성되는 방식은 매우 간단합니다.
id 속성은 모든 HTML 요소에 사용할 수 있으며, id 속성은 Canvas에는 다음 두 개가 있습니다(각각 너비와 높이를 제어함). 호환성에 대해 CanIUse는 현재 사용자가 사용하는 브라우저의 90%에서 기본 기능을 지원하므로 대부분의 경우 안심하고 사용할 수 있다고 위에서 언급했습니다.
캔버스와 함께 제공되는 너비 및 높이 속성을 사용해야 하며 CSS를 사용하여 제어하면 안 됩니다. CSS 제어로 인해 캔버스가 변형되기 때문입니다. PhptpShop과 비교해 볼 수 있습니다. 후자는 "이미지 크기"를 변경하는 반면 전자는 "캔버스 크기"를 변경하는 올바른 방법입니다. 예를 들어 다음 그림은 세 장의 그림을 가로로 이어붙인 것입니다. 가장 왼쪽의 검은색 상자는 50px * 50px 크기의 원본 그림이고, 가운데는 이미지 크기를 100px * 100px로 변경한 효과입니다. 하지만 이미지 자체의 경우 좌표 범위가 더 커지지 않았다고 합니다. 가장 오른쪽이 올바른 100px * 100px Canvas입니다.
캔버스 대부분의 그리기 방법은
먼저 이 요소를 얻습니다:
var canvas = document.getElementById('canvas');
그런 다음 메서드를 사용하여 모든 Canvas API를 호출할 수 있는 입구를 얻습니다.
var ctx = canvas.getContext('2d');
2d가 I인지 확인합니다. 3D가 있는지 생각하게 되어 매우 기뻤습니다. 3D를 작성할 수 있는 방법은 없지만 3D 세계로의 문을 열고 싶다면 canvas.getContext('webgl')를 작성하면 됩니다. 하지만 WebGL은 OpenGL ES 2.0을 기반으로 한 표준 집합으로, 본 글과는 전혀 다르기 때문에 여기서는 다루지 않겠습니다.
캔버스의 기본 개념
좌표
는 수학에서 흔히 사용하는 데카르트 좌표계와는 다릅니다. 캔버스의 좌표계는 컴퓨터에서 흔히 사용되는 좌표계입니다. 다음과 같습니다:
캔버스의 왼쪽 위 모서리는 (0,0)이고, x는 오른쪽으로 증가하고, y는 아래로 증가하며, x와 y는 모두 정수입니다. (계산시에는 정수가 아니더라도 그릴 때에는 정수로 처리됩니다.) 단위는 픽셀입니다.
绘图
带大家怀旧一下。不知道有多少同学小时候玩过 logo 语言,在里面你可以控制一只小海龟在一块板子上行走、画画、提笔、落笔。Canvas 中也一样,你需要控制一只画笔的移动和绘制。然而 Canvas 更高级一些,你可以直接利用一些函数来画图,不用去控制那只画笔的位置。
Canvas 中的基本图形
通过上文定义的 ctx 变量可以干许多有意思的事情,我们先看看如何绘制一些基本图形。
线条
我们指定画笔移动到某一点,然后告诉画笔需要从当前这一点画到另一点。我们可以让画笔多次移动、绘制,最后统一输出到屏幕上。例子如下:
ctx.moveTo(10, 10); ctx.lineTo(150, 50); ctx.lineTo(10, 50); ctx.moveTo(10, 20); ctx.lineTo(40, 70); ctx.stroke();
上面的代码中,lineTo 是产生线条用的函数,执行完之后画笔就移到了线条的终点。需要注意的是,线条此时并没有显示在屏幕上,必须调用 stroke 才会显示。这样设计是有道理的,因为向屏幕上输出内容需要耗费大量的资源,我们完全可以先攒够一波 lineTo,最后用 stroke 放一个大的。
路径
绘制路径非常简单,只需要先告诉 ctx 一声“我要开始画路径了”,然后通过各种方法(例如 lineTo)绘制路径。如果需要画一个封闭路径,那就最后告诉 ctx一声:“我画完了,你把它封闭起来吧。”当然,不要忘记利用 stroke 输出到屏幕上。
一个简单的例子:
ctx.beginPath(); ctx.moveTo(10, 10); ctx.lineTo(150, 50); ctx.lineTo(10, 50); ctx.closePath(); ctx.stroke();
如果我不想只描绘路径线条,而是想填充整个路径呢?可以将最后一行的 stroke 改成 fill,这样就跟使用了画图中的油漆桶一样,封闭路径里面的内容就都被填充上颜色了:
ctx.fill();
弧 / 圆形
绘制弧的函数参数比较多:
ctx.arc(圆心 x 坐标, 圆心 y 坐标, 半径, 起始角度, 终止角度, 是否为逆时针);
注意,在 Canvas 的坐标系中,角的一边是以圆心为中心的水平向右的直线。角度单位均为弧度。例如下图,确定了圆心、起始角度(图中标明的锐角)和终止角度(图中标明的钝角),方向为逆时针,于是就有了这么一个弧。如果方向为顺时针,那么就会是一个跟它互补的、非常非常大的弧……
所以如果转了 2π 圈之后,弧就成了圆形,因此也可以使用绘制弧的方式来绘制圆形:
ctx.beginPath(); ctx.arc(圆心 x 坐标, 圆心 y 坐标, 半径, 0, Math.PI * 2, true); ctx.closePath();
最后一个参数随便填(当然也可以不填),因为不管是顺时针还是逆时针,转了 2π 圈之后都是一个圆。
矩形
如果只是想绘制一个横平竖直的矩形,可以使用下面的两个方法:
// 只描边 ctx.strokeRect(左上角 x 坐标, 左上角 y 坐标, 宽度, 高度); // 只填充 ctx.fillRect(左上角 x 坐标, 左上角 y 坐标, 宽度, 高度);
线条样式 / 填充样式
之前绘制的所有图形都是黑色的,但是 Canvas 肯定不止这么一种颜色(不然标准的制定者会被喷的很惨)。事实上,Canvas 可以单独设置线条样式和填充样式,分别使用的是 strokeStyle 和 fillStyle。可能的值有三种:纯色、渐变、图像。既然线条样式与填充样式的使用方法相同,那么下面统一以填充样式为例。如果想设置线条样式,直接将所有的 fillStyle 改成 strokeStyle 即可,里面的参数都不变。
/* 纯色填充 */ // 普通的颜色 ctx.fillStyle = '#0000ff'; // 带有透明度的颜色 ctx.fillStyle = 'rgba(64, 0, 127, 0.5)'; /* 渐变填充 */ // 设置渐变的尺寸(参数分别为起始点的 x 和 y、终止点的 x 和 y) var gradient = ctx.createLinearGradient(0, 0, 170, 0); // 设置过渡色,第一个参数是渐变的位置,第二个参数是颜色 gradient.addColorStop(0, 'magenta'); gradient.addColorStop(0.5, 'blue'); gradient.addColorStop(1.0, 'red'); // 设置填充样式 ctx.fillStyle = gradient; /* 图片填充 */ // 创建图片 var image = new Image; image.src = '/path/to/image.png'; // 创建图片笔触,可以指定图片的平铺方式,这里是横向平铺 var pattern = ctx.createPattern(image, 'repeat-x'); // 设置笔触填充 ctx.fillStyle = pattern;
关于渐变,除了代码中提到的线性渐变以外,还有 createRadialGradient,也就是径向渐变。
设置完填充样式之后,就可以使用 fill 来填充啦!如果设置的是线条样式,那么就可以使用 stroke 来描边。
当然,对于线条样式,还有个额外的方法叫 lineWidth 可以用来控制线条的宽度。
文字
要想在画布上画文字,首先需要知道所使用的字体和字号:
ctx.font = '30px Verdana';
然后就可以通过 strokeText 或者 fillText 来对字体描边或者填充字体。
ctx.strokeText("Hello Coding!", 23, 33); ctx.fillText("Hello Coding!", 23, 66);
图片
在 Canvas 中绘制图片有三种方法:
// 指定绘制位置 ctx.drawImage(image, x, y); // 指定绘制位置和图像宽高 ctx.drawImage(image, x, y, width, height); // 指定剪裁区域、绘制位置和图像宽高 ctx.drawImage(image, sx, sy, swidth, sheight, x, y, width, height);
参数的含义依次如下:
image: 要使用的 Image、Canvas 或 Video sx: 可选,开始剪切的 x 坐标 sy: 可选,开始剪切的 y 坐标 swidth: 可选,被剪切图像的宽度 sheight: 可选,被剪切图像的高度 x: 在画布上放置图像的 x 坐标 y: 在画布上放置图像的 y 坐标 width: 可选,要使用的图像的宽度 height: 可选,要使用的图像的高度
画布设置
细心的同学可能会发现,刚才有些属性是直接对 ctx 变量做设置,例如 ctx.lineWidth,只要设置了它,那么后续画出来的线条全都是这么个宽度。
其实,Canvas 的设置项还有许多,例如我们可以直接移动画布、旋转画布、设置全局的绘制透明度等等。这些设置还可以随时保存和恢复。
要注意的一点是,所有已经画在画布上的东西,是已经定死了的,不管之后再次进行任何设置都不会再改变。这个很像 Windows 下的画图程序。
废话不多说,直接上代码:
// 移动画布,其实就是移动坐标系 ctx.translate(往右移动的量, 往下移动的量); // 旋转画布,旋转中心为坐标系原点 ctx.rotate(顺时针旋转的角度); // 以坐标系原点为中心缩放画布 ctx.scale(横向放大倍数, 纵向放大倍数); // 设置绘制透明度,如果 fillStyle 等属性设置了透明度则会叠加 ctx.globalAlpha(零到一的小数); // 设置全局组合操作 ctx.globalCompositeOperation = 'lighter'; // 保存当前设置 ctx.save(); // 恢复上次保存的设置 ctx.restore();
移动、旋转、缩放其实就是在控制绘图的坐标系,如果你在调用这三个方法的时候,脑子里时刻有一个带刻度的坐标系,效果会非常好。
사실 Canvas의 좌표 변환은 컴퓨터 그래픽의 지식인 변환 행렬을 따릅니다. 간단히 말하면, 좌표는 행렬로 간주될 수 있으며, 좌표 변환은 해당 좌표에 해당하는 행렬을 변환 행렬과 곱하여 구현할 수 있습니다. 계산의 효율성을 높이기 위해 먼저 여러 변환을 결합한 후 변환 행렬을 계산한 다음 변환 함수를 통해 현재 좌표계를 직접 변환하거나 setTransform 함수를 통해 좌표계를 초기 상태로 재설정한 후 수행할 수 있습니다. 변형. 변환 행렬의 내용은 이 기사의 범위를 약간 벗어납니다.
전역 조합 작업은 PhotoShop의 "혼합 옵션"과 약간 비슷합니다. 구체적인 구현 방법은 아직 완전히 결정되지 않았습니다. 현재 일반 브라우저에는 소스 오버, 소스 상단, 대상 등의 통합된 구현 방법이 있습니다. - 오버, 목적지 아웃, 라이터, xor. 구체적인 동작은 Mozilla 공식 문서에서 확인할 수 있지만 표준이 아직 완전히 결정되지 않았기 때문에 다른 브라우저에서는 모든 동작이 Mozilla 표준과 일치한다고 보장하지 않습니다. 일반적으로 말하면, 더 일반적인 것은 소스 오버이고 더 가벼우며, 이 두 가지 표준은 브라우저 업계에서 논쟁의 여지가 없습니다.
설정을 저장하고 복원하는 작업이 좀 재미있습니다. 먼저 '스택'이라는 것을 이해해야 합니다.
스택은 한 방향에서만 연산이 가능한 1차원 배열입니다. 스택은 처음에는 비어 있습니다. 이 방향에서 요소를 배열로 밀어 넣을 수 있으며 이 방향에서는 마지막 요소(스택의 최상위 요소)만 팝할 수 있습니다. 이 외에 추가 작업은 없습니다. 물론, 팝의 개수는 푸시의 개수보다 클 수 없습니다. 팝이 스택의 맨 아래에 도달하면 스택에 요소가 하나도 없고 이때 다시 팝하는 것은 의미가 없기 때문입니다. 스택은 대괄호 일치, 표현식 평가, 깊이 우선 검색 등 다양한 용도로 사용되며 대부분의 언어에서 스택을 사용하는 함수 호출에도 사용됩니다.
저장 함수를 호출할 때마다 실제로 현재 전역 설정을 특수 스택에 푸시합니다. 복원 함수를 호출할 때마다 마지막으로 저장된 콘텐츠를 꺼내서 현재 전역 설정을 덮어씁니다. . , 스택의 맨 위가 가장 최근에 저장된 콘텐츠입니다. 예를 들어, 구부러진 도형을 그린 다음 계속해서 똑바로 선 도형을 그려야 하는 경우에는 저장하고 복원하는 것이 유용합니다. 이런 식으로 먼저 저장을 호출한 다음 도형을 그린 후 복원하고 계속할 수 있습니다. .다른 모양을 그립니다.
사실 Canvas에는 많은 메서드가 있습니다. 예를 들어 toDataURL은 현재 캔버스의 콘텐츠를 16진수 데이터 URL로 직접 변환하고, getImageData는 이미지 처리 알고리즘에서 사용할 수 있도록 이미지를 RGBA 배열로 직접 변환하며, putImageData는 RGBA 배열을 그림으로 변환하여 캔버스 등에 표시합니다. 정기적인 JavaScript 업데이트(setInterval 대신 requestAnimationFrame 사용 권장)와 결합하면 애니메이션 효과를 생성할 수 있습니다. 또한 프로그래머가 Canvas를 기반으로 자신만의 특수 효과나 기능을 더 쉽게 작성할 수 있도록 하는 많은 Canvas 라이브러리가 인터넷에 있습니다. 여기서 말씀드리고 싶은 점은 모든 사람의 상상력이 얼마나 강력한지, 캔버스가 얼마나 강력한지~