共计 3318 个字符,预计需要花费 9 分钟才能阅读完成。
2022-11-07
1. 反向绘制
fill(),方法用于填充已有的闭合路径,假设有一个如下图的路径,默认情况下圆和方形都会被填充,最后的效果就是一个黑色的方形;
demo
通过 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 放大两倍刚好抵消掉。
提示
canvas 绘图时,会从两个物理像素的中间位置开始绘制并向两边扩散 0.5 个物理像素。当设备像素比为 1 时,一个 1px 的线条实际上占据了两个物理像素(每个像素实际上只占一半),由于不存在 0.5 个像素,所以这两个像素本来不应该被绘制的部分也被绘制了,于是 1 物理像素的线条变成了 2 物理像素,视觉上就造成了模糊
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 导出,分辨率相对来说就高了。
小贴士
离屏 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
方大之后这个判断方法仍然有效,但是旋转之后,图形有一部分就点不到了。
2.2 isPointInPath
不管是方形、图片、还是其他的,描绘一个相同的路径(位移、旋转、缩放),都可以进行如下判断
context.beginPath();
context.rect(x, y, w, h);
context.closePath();
this.clicked = context.isPointInPath(point.x, point.y);
点击判断
绘制图形时,可以同步绘制一个形状一致的路径,然后通过 isPointInPath 判断是否被点击
2.3 离屏绘制
每个图形在离屏画布上绘制一个大小状态一样的图片,并使用唯一的颜色值填充,这个颜色值就代表这个图形的索引。
问题记录
1. 学习总结
- canvas 初始化之后,就一直在运行
window.requestAnimationFrame
,之后只有更改图形的属性,就会自动刷新。 - 如果在浏览器中创建多个 Image 对象并加载同一张图片,浏览器不会重复加载该图片。浏览器会将该图片缓存到内存中,并在需要时从缓存中获取它。