Canvaskit 中矩阵的使用

194 阅读5分钟

大家好,我是前端西瓜哥。

canvaskit 类似 Canvas 2D,会维护画布的 当前矩阵(current matrix),之后通过 drawXx 渲染图形时,会应用这个矩阵的值对顶点做一次变换计算,基于新的点位置再绘制出来。

在合适的时机修改这个矩阵,我们就可以实现视图矩阵变换、模型变化等各种矩阵变换效果。

基本的变形矩阵

  • translate(dx, dy):画布的当前矩阵乘以位移矩阵;

  • rotate(rot, rx, ry):乘以旋转矩阵,角传入的是角度,不是弧度,同时需要指定旋转中心;

  • scale(sx, sy):乘以缩放矩阵;

  • skew(sx, sy):乘以斜切矩阵。

示例:

const canvas = surface.getCanvas();

canvas.translate(100100); // 位移 100, 100
canvas.rotate(3000); // 旋转 30 度
canvas.scale(22); // 缩放 2 倍

canvas.drawRect(rect, redPaint);

一个 [0, 0, 100, 100] 的矩形,缩放为 2 倍,然后基于原点 0, 0 旋转 30 度,最后位移 100, 100,效果见下图。

图片

乘以任意矩阵

concat(InputMatrix):左乘一个任意矩阵。这个矩阵是 行矩阵,所谓行矩阵就是一行行把矩阵的值放到一位数组里来做表达。

// 等价 canvas.scale(2, 2)

const m = [200020];
canvas.concat(m);

入参的类似就很丰富了,下面这些都行。

type InputMatrix = MallocObj | Matrix4x4 | Matrix3x3 | Matrix3x2 | DOMMatrix | number[];

获取当前矩阵的值

getTotalMatrix() 会返回画布的当前矩阵,是一个完整的 3x3 矩阵,把可以省略的 [0, 0, 1] 补上了。

canvas.getTotalMatrix()

// 返回值:
[    3.464101552963257, -2, 100,    2, 3.464101552963257, 100,    0, 0, 1]

矩阵工具方法

有时候我们希望拿到矩阵数据进行计算,canvaskit 有提供一些工具方法。

注意这些方法返回的是完整的 3x3 矩阵数组。

  • CanvasKit.Matrix.identity():返回单位矩阵数组;

  • CanvasKit.Matrix.multiply(m1, m2, m3, ...):返回多个 3x3 矩阵进行顺序相乘的结果;

  • CanvasKit.Matrix.translated(dx, dy):返回位移矩阵;

  • CanvasKit.Matrix.scaled(sx, sy, px, py):返回缩放矩阵,(px, py) 是变形中心;

  • CanvasKit.Matrix.rotated(rad, px, py):返回基于点 (px, py) 旋转 rad 弧度的旋转矩阵;

  • CanvasKit.Matrix.skewed(kx, ky, px, py);:返回斜切矩阵;

  • CanvasKit.Matrix.invert(m):返回矩阵的逆矩阵;

  • CanvasKit.Matrix.mapPoints(m, points):返回应用了矩阵后的所有点。这些点是用一维数组拍平表达的。

直接修改矩阵

skia 里是有个 setMatrix 的方法用来直接修改当前矩阵矩阵值的方法,但是 canvaskit 并没有暴露出来,都是些在原来的矩阵上乘上一个矩阵的方法。

一个办法是根据逆矩阵的定义,我们给当前矩阵乘上它的逆矩阵,就得到了单位矩阵,然后 concat 其他矩阵,就变成其他矩阵了。

const currentMatrix = canvas.getTotalMatrix();
const invertMatrix = CanvasKit.Matrix.invert(currentMatrix);

canvas.concat(invertMatrix);
canvas.concat(newMatrix);

或者在一开始的时候 store 一下。store 方法会保存当前 matrix 的 clip 信息,此时 matrix 就还是单位矩阵。然后后面要重置的时候 restore 一下,matrix 就还原成单位矩阵了。

canvas.save();

// ...

canvas.restore(); // 恢复原来的单位矩阵
canvas.concat(newMatrix);

最后但可能是最好的办法,就是自己编译 skia,把 setMatrix 方法还有其他你相用的方法暴露出来,编译成 wasm。

结尾

我是前端西瓜哥,关注我,学习更多前端图形知识。


相关阅读,

Canvaskit 入门,绘制基本图形

canvaskit 高级功能:布尔运算

Web 端图形渲染方案这么多,到底该选哪一种?

Canvas 简单入门小教程

一起学 WebGL:复合矩阵