canvas随手记(持续)

  • start date: 2018-08-27 13:00:45

1.canvas的width/height属性

canvas的width/height属性定义了canvas元素的尺寸(渲染上下文的尺寸),默认为widht: 300px。height: 150px。

2.canvas上下文的坐标系统

2D渲染上下文采用平面的笛卡尔坐标系统,左上角为圆点(0, 0),向右移动时,x坐标值会增加;向下移动时,y坐标值会增加。

coordinate.png

3.矩形绘制fillRect/strokeRect

fillRect绘制一个矩形并给它填充颜色;strokeRect绘制一个矩形并绘制边框。

4.角度单位

canvas的角度是以弧度为单位的,如360°->。计算公式

1
2
let degrees = 1;	// 1°
let radians = degrees * (Math.PI / 180); // 弧度

radians.png

5.画圆Canvas arc()函数

1
context.arc(x, y, r, sAngle, eAngle, counterclockwise);

参数

  • x:圆的中心的 x 坐标。
  • y:圆的中心的 y 坐标。
  • r:圆的半径。
  • sAngle:起始角,以弧度计。(弧的圆形的三点钟位置是 0 度)。
  • eAngle:结束角,以弧度计。
  • counterclockwise:可选。规定应该逆时针还是顺时针绘图。False = 顺时针,true = 逆时针。

counterclockwise虽然是可选的,但如果不传入这个参数,Firefox会抛出一个错误。

6.用于描述画布绘图状态的全部属性为:变换矩形、裁剪区域(clipping region)、globalAlpha、globalCompositeOperation、strokeStyle、fillStyle、lineWidth、lineCap、lineJoin、miterLimit、shadowOffsetX、shadowOffsetY、shadowBlur、shadowColor、font、textAlign、textBaseline。

画布的当前路径和当前位图并不属于状态。

7.变换矩形

所有变形方法(translate/scale/rotate)都会影响变换矩形。变换矩形就是一组数字,他们各自描述一个特定变形类型。矩阵分成多个列和行;在画布中,你使用的是一个3*3矩阵。

transformRect.png

一个新的2D渲染上下文将包含一个全新的变换矩阵,即单位矩阵(identity matrix)

identitymatrix.png

transform

canvas transform方法有6个参数,分别对应变换矩阵的每一个值:

1
context.transform(xScale, ySkew, xSkew, yScale, xTrans, yTrans);

setTransform

setTransform的作用是将矩阵重置为单位矩阵,然后按照6个参数执行变形。

8.合成属性globalCompositeOperation

  • ‘source-over’:默认值,表示绘制的图形(源)将画在现有画布(目标)之上;
  • ‘destination-over’:表示目标在源之上;
  • ‘source-atop’:表示源绘制在目标之上,但重叠区域上源是透明的目标不透明;
  • ‘destination-atop’:表示目标绘制在源之上,但在重叠区域上目标是透明的源不透明;
  • ‘source-in’:表示在源和目标重叠的区域只绘制源,但不重叠的部分变成透明的;
  • ‘destination-in’:表示在源和目标重叠的区域只绘制目标,但不重叠的部分变成透明的;
  • ‘source-out’:表示在与目标不重叠的区域绘制源,其他部分变成透明的;
  • ‘destination-out’:表示在与源不重叠的区域保留目标,其他部分变成透明的;
  • ‘lighter’:表示如果源与目标重叠,就将两者的颜色值相加(最大值为白色255)。
  • ‘copy’:表示只绘制源,覆盖目标。
  • ‘xor’:表示只绘制不重叠的源与目标区域。所有重叠的部分都变成透明的;

9.阴影和渐变属性

阴影

  • shadowBlur、shadowOffsetX、shadowOffsetY、shadowColor。

渐变方法

  • createLinearGradient、addColorStop

10.贝塞尔曲线

canvas中Bezier curve方法:quadraticCurveTo(二次)、bezierCurveTo(三次)。

这两种贝塞尔曲线都是通过控制点将一条直线变成曲线。二次贝塞尔曲线只有一个控制点,这意味着线条中只有一次弯曲;而三次贝塞尔曲线则有两个控制点,这意味着一条线中会有两次弯曲。

bezier.png

11.drawImage之前需要确认图像已经完全加载。


1
2
3
4
5
6
let _img = new Image();
_img.onload = function () {
ctx.drawImage(_img, 0, 0, 100, 100);
};

_img.src = 'http://blog.michealwayne.cn/test.png'

12.ImageData像素信息集

通过访问2D渲染上下文的各个像素,我们就能得到每一个像素的颜色和阿尔法值等信息。

在画布中访问像素的方法是getImageData()。

1
context.getImageData(x, y, width, height);

这个方法接受4个参数:要访问的像素区域原点坐标(x, y)、像素区域的宽度和高度。

返回的ImageData包含3个属性:

  • width:访问像素区域的宽度
  • height:访问像素区域的高度
  • data:包含所访问区域中全部像素信息的CanvasPixelArray

其中data属性是一个一维数组。数组中每个像素用4个整数值表示,范围从0至255,分半表示红、绿、蓝和阿尔法值(rgba)

imagedata.png

当然我们也可以处理或自定义实现像素数组,然后通过putImageData方法进行图片绘制或创建。

反转颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
let imagedata = context.getImageData(0, 0, canvas.width, canvas.height);
let pixels = imagedata.data;

let numPixels = pixels.length;
ctx.clearRect(0, 0, canvas.width, canvas.height);

for (let i = 0; i < numPixels; i++) {
pixels[i * 4] = 255 - pixels[i * 4]; // 红
pixels[i * 4 + 1] = 255 - pixels[i * 4 + 1]; // 绿
pixels[i * 4 + 2] = 255 - pixels[i * 4 + 2]; // 蓝
}

ctx.putImageData(imagedata, 0, 0);

灰度

1
2
3
4
5
6
for (let i = 0; i < numPixels; i++) {
var average = (pixels[i * 4] + pixels[i * 4 + 1] + pixels[i * 4 + 2]) / 3;
pixels[i * 4] = average; // 红
pixels[i * 4 + 1] = average; // 绿
pixels[i * 4 + 2] = average; // 蓝
}

像素化

`` js
let numTileRows = 20;
let numTileCols = 20;

let tileWidth = imagedata.width / numTileCols;
let tileHeight = imagedata.height / numTileRows;

for (let r = 0; r < numTileRows; r++) {
for (let c = 0; c < numTileCols; c++) {
let x = (c tileWidth) + (tileWidth / 2);
let y = (r
tileHeight) + (tileHeight / 2);

    let pos = Math.floor(y) * (imagedata.width * 4) + Math.floor(x) * 4;

    ctx.fillStyle = `rgb(${pixels[pos]}, ${pixels[pos + 1]}, ${pixels[pos + 2]})`;
    ctx.fillRect(x - tileWidth / 2, y - tileHeight / 2, tileWidth, tileHeight);
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13


## 13.自制拾色器
``` js
canvas.onclick = e => {
let canvasOffset = canvas.offset();
let canvasX = Math.floor(e.pageX - canvasOffset.left);
let canvasY = Math.floor(e.pageY - canvasOffset.top);

let imagedata = context.getImageData(canvasX, canvasY, 1, 1);
let pixel = imagedata.data;
console.log(`rgba(${pixel[0]}, ${pixel[1]}, ${pixel[2]}, ${pixel[3]})`)
};

14.图像限制

如果图像与控制画布的js不在同一个域名,那么画布对于访问这个图像的像素级数据会有严格的限制。

15.动画循环

动画循环是创建动画效果的基础。它的三要素是:更新需要绘制的对象、清除画布、在画布上重新绘制对象。

animatecircle.png

在没有requestAnimationFrame的时候请使用33ms

因为动画在每秒钟需要的帧数通常介于25到30帧之间,1000/30≈33

16.canvas图片限制及启用CORS

在canvas画布中使用跨域图片,此时会污染画布,而且画布一旦被污染,你就无法读取其数据。例如你不能再使用画布的 toBlob(), toDataURL() 或 getImageData() 方法,调用它们会抛出安全错误。

这种机制可以避免未经许可拉取远程网站信息而导致的用户隐私泄露。

HTML 规范中图片有一个 crossorigin 属性,结合合适的 CORS 响应头,就可以实现在画布中使用跨域 元素的图像。

crossOrigin属性

在HTML5中,一些 HTML 元素提供了对 CORS 的支持, 例如

  • anonymous:对此元素的CORS请求将不设置凭据标志。
  • use-credentials:对此元素的CORS请求将设置凭证标志; 这意味着请求将提供凭据。

启用CORS

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF。

你必须有一个可以对图片响应正确 Access-Control-Allow-Origin 响应头的服务器。如

1
2
3
4
5
6
7
8
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>

配置完毕后,你就可以将这些图片保存到 DOM 存储 中了,就像这些图片在你自己域名之下一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var img = new Image,
canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
src = "http://example.com/image"; // insert image url here

img.crossOrigin = "Anonymous";

img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage( img, 0, 0 );
localStorage.setItem( "savedImageData", canvas.toDataURL("image/png") );
}
img.src = src;
// make sure the load event fires for cached images too
if ( img.complete || img.complete === undefined ) {
img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
img.src = src;
}

17.移动端1px出现模糊的解决方法

先上解决方法

1
2
cxt.moveTo(x + 0.3,y + 0.3);	// 小数
cxt.lineTo(x + 0.3, y + 0.3)

这样确实可以让线重新变回清晰的状态
因为把绘制线条封装成一个函数,绘制多条线,多次调用该函数,出现有的线是1px,有的线是2px。

原因

canvas每条线都有一条无限细的中线,线由中线两个伸展。例如:
当起点是2px时,中线在2px的地方,向左延伸0.5px,向右延伸0.5px,所以这条线应该在1.5px到2.5px的地方,但实际上计算机的最小像素是1px,所以canvas会取一个折中的方法,分别再向左右延伸0.5px,颜色变成原来的一半,所以实际效果看起来变成了2px模糊的线条。

1px.png