Canvas学习笔记,记录使用过程中遇到的一些问题
- canvas
- 2022-11-07
- 1135热度
- 0评论
2022-11-07
1.反向绘制
fill(),方法用于填充已有的闭合路径,假设有一个如下图的路径,默认情况下圆和方形都会被填充,最后的效果就是一个黑色的方形;
通过fillRule参数,可以指定填充的算法,决定点是在路径内还是在路径外。 允许的值:
- "nonzero": 非零环绕规则,默认的规则。
- "evenodd": 奇偶环绕规则。
将填充规则设置为evenodd,绘制的结果会变为下面这样,通过这种方式可以实现反向裁剪。
通过destination-out
的擦除效果,同意可以实现上面的效果
/* 保存状态 */
context.save();
context.fillStyle = "rgba(246,246,246,0.6)"; //背景色
context.fillRect(0, 0, canvas_w, canvas_h); //画圆
context.globalCompositeOperation = "destination-out"
context.fillStyle = "rgba(255,255,255,1)"; //背景色
context.fillRect(x, y, w, h); //画圆
context.restore(); //恢复状态
2.原地旋转
默认的旋转是通过改变坐标系的角度实现的(矩阵),所以旋转后中心点不会在原来的地方;
/* 角度换算弧度 */
let rotateRadius=45*Math.PI/180
/*计算方形的中心点 */
let rectCenterPoint = {
x: x + w / 2,
y: y + h / 2
};
/* 旋转的同时,让中心点回到原来的位置 */
context.translate(rectCenterPoint.x, rectCenterPoint.y);
context.rotate(rotateRadius); //旋转
context.translate(-rectCenterPoint.x, -rectCenterPoint.y);
3.原地缩放
默认的旋转是通过改变坐标系的刻度实现的(矩阵),所以缩放后中心点不会在原来的地方;
/* 倍数 */
let scaleRadio=1.5
/*计算方形的中心点 */
let rectCenterPoint = {
x: x + w / 2,
y: y + h / 2
};
/* 中心点复位 */
context.translate(
rectCenterPoint.x * (1 - scaleRadio),
rectCenterPoint.y * (1 - this.scaleRadio));
context.scale(scaleRadio, scaleRadio);
4.clearRect
如果没有依照 绘制路径 的步骤(begin、close),使用 clearRect() 会导致意想之外的结果(线条乱窜),在调用 clearRect()之后绘制新内容前调用beginPath() 。
5.drawImage模糊
在 iPhone3G 时代,屏幕宽度是 320px,其宽度上的物理像素也是 320px;而到了 4s 时代,屏幕宽度依然是 320px,但是宽度上的物理像素却变成了 640px,是宽度的两倍
屏幕宽度没变,物理像素却增加了,所以为了屏幕显示的内容不改变,原先需要一个像素绘制的点,现在会用两个像素来绘制,为了表示这种屏幕的特性,浏览器全局对象下就有了这样一个属性——devicePixelRatio设备像素比,它的计算方式是 物理像素 / 屏幕宽度的像素;
- 首先设置canvas的宽度和高度是原来的2倍
- 使用ctx.scale(2,2)设置绘制的东西也放大2倍
- 在canvas的父元素上使用缩放,使用css3的 transform:scale(0.5,0.5)即可,意思为缩放到原来的2倍大小,和canvas放大两倍刚好抵消掉。
6.分层渲染
将多个canvas叠在一起,通过设置每个canvas的 z-index 达到多个画布还是在同一层的错觉;
7.局部渲染
静态画面,本身就可以清空指定区域,然后指定那个区域重新绘制新图行。
8.离屏渲染
OffscreenCanvas提供了一个可以脱离屏幕渲染的canvas对象。它在窗口环境和web worker环境均有效。(一个不在屏幕上实际显示的画布)
// 离屏canvas
const offscreen = new OffscreenCanvas(200, 200);
通过transferToImageBitmap函数可以从OffscreenCanvas对象的绘制内容创建一个ImageBitmap对象。该对象可以用于到其他canvas的绘制。
9.矩阵变换
向量是有长度及方向的量,一般由多个标量(scalar,即单纯的数字)组合而成。比如由两个标量组合而成的二维向量,可以表示二维空间(平面)中有长度及方向的量。由三个标量组成的三维向量,可以表示三维空间中具有长度及方向的量;
10.touchmove
触发touchstart事件之后,假如move的距离特别小,就不会触发toucemove事件。
所以图形拖动的时候,以touchstart事件的坐标作为拖动的参照点时会产生偏差,结果就是拖动开始的时候,图形会瞬移一段距离。
可以将参照点的坐标调整为第一次touchmove事件触发时的坐标。
11.move和requestAnimationFrame
在大多数情况下,mousemove、touchmove事件的触发频率会比 window.requestAnimationFrame 高得多。
所以在绘制手写线条时,需要监听mousemove事件,使用requestAnimationFrame 会出现线条断层的情况。
2022-11-09
1. canvas导出模糊
移动端受限于屏幕,实际能看到的画布只有手机的大小,按照手机的分辨率导出canvas作为图片,分辨率确实太低了。
可以在导出之前将canvas的长宽放大指定倍数,同时将图形放大,之后再通过toDataURL导出,分辨率相对来说就高了。
2022-11-15
1.放大canvas
canvas的分辨率是实际分辨率的两倍,此时保持图形正常绘制,原点,大小都要跟着放大。
屏幕坐标换算到画布上需要乘以放大的倍数。
2.图形选中
2.1 范围判断
以正方形为例,正常情况下可通过如下算法去判断图形是否被点击(点击point,图形rect)。
if (
point.x > rect.x &&
point.x < rect.x + rect.w &&
point.y > rect.y &&
point.y < rect.y + rect.h
) {
return true;
} else {
return false;
}
方大之后这个判断方法仍然有效,但是旋转之后,图形有一部分就点不到了。
2.2 isPointInPath
不管是方形、图片、还是其他的,描绘一个相同的路径(位移、旋转、缩放),都可以进行如下判断
context.beginPath();
context.rect(x, y, w, h);
context.closePath();
this.clicked = context.isPointInPath(point.x, point.y);
2.3 离屏绘制
每个图形在离屏画布上绘制一个大小状态一样的图片,并使用唯一的颜色值填充,这个颜色值就代表这个图形的索引。
问题记录
1.学习总结
- canvas初始化之后,就一直在运行
window.requestAnimationFrame
,之后只有更改图形的属性,就会自动刷新。 - 如果在浏览器中创建多个Image对象并加载同一张图片,浏览器不会重复加载该图片。浏览器会将该图片缓存到内存中,并在需要时从缓存中获取它。