【Fabric.js 系列】Fabric.js 是如何实现元素的平移、旋转、缩放的

如何实现元素的平移、旋转、缩放

在上一节中,我们知道了元素是如何渲染到 Canvas 的画布上,这一节我们将了解如何对元素进行平移、旋转、缩放。

如何平移、旋转、缩放元素

在上一节我们知道,元素都继承自 fabric.Object,而 fabric.Object 的 render 方法都由各个元素实现。那么对于元素的平移、旋转、缩放,我们又需要如何抽离出通用的逻辑呢?

不妨思考一下,这个案例

假设要在 (100, 100) 的地方绘制一个 50*50 的矩形,并将其放大 2 倍,之后旋转 45°,该怎么画呢?

正常逻辑是:

  1. 手动算下宽高 100 * 100

  2. 手动算下旋转之后各个顶点的坐标

  3. 连接四个顶点,绘制矩形

也就是我们先手动先计算出各个转化后的坐标,通过坐标绘制出图形。

但在 Canvas 中,要改掉这种绘制的思想,而是要通过并善用变换坐标系来绘制物体

思路如下:

1 绘制一个 50*50 的矩形

2 转化坐标系

代码如下:

js 复制代码
ctx.save(); // 之前提到过了,你要修改 ctx 上的一些配置或者画一个物体,最好先 save 一下,这是个好习惯
ctx.translate(100, 100); // 此时原点已经变到了 (100, 100) 的地方
ctx.scale(2, 2); // 坐标系放大两倍
ctx.rotate(Util.degreesToRadians(45)); // 注意 canvas 中用的都是弧度(弧度 / 2 * Math.PI = 角度 / 360),所以需要简单换算下
ctx.fillRect(-width/2, height/2, width, height); // 绘制矩形的方法固定不变,宽高一般也不会去修改
ctx.restore(); // 画完之后还原 ctx 状态,这是个好习惯

尽量不去改变物体的宽高和大小,而是通过各种变换来达到所需要的效果

矩阵运算

在 Fabric.js 中,并不是通过 translate、scale、rotate 这些方法来实现元素的平移、旋转、缩放,而是通过矩阵运算来实现。

通过 transform api 做矩阵变化,参数 是一个数组,里面有6个元素,默认值是 1, 0, 0, 1, 0, 0。

js 复制代码
[0]: 水平缩放(x轴方向)
[1]: 水平倾斜(x轴方向)
[2]: 垂直倾斜(y轴方向)
[3]: 垂直缩放(y轴方向)
[4]: 水平移动(x轴方向)
[5]: 垂直移动(y轴方向

1 修改 viewportTransform0、viewportTransform3 达到缩放横纵坐标的值

2 修改 viewportTransform4、viewportTransform5 达到平移画布的效果

对于上面这个例子,我们只需要修改 viewportTransform 的值,然后重新渲染画布就好了。

js 复制代码
const width = 50;
const height = 50;
const angle = (45 / 180) * Math.PI; // Math.PI / 4
ctx.save();
ctx.transform( // 旋转
  2 * Math.cos(angle),
  Math.sin(angle),
  -Math.sin(angle),
  2 * Math.cos(angle),
  100,
  100,
);
ctx.fillRect(-width/2, height/2, width, height); // 绘制矩形的方法固定不变,宽高一般也不会去修改
ctx.restore();

同样的,在 fabric.js 中,在 render 方法中,会调用 transform 方法,将 viewportTransform 的值设置为 transform 的值,以达到对元素进行平移、旋转、缩放的效果。

如何实现漫游

通过监听滚轮事件,然后修改 viewportTransform 的值,以达到对画布进行平移的效果。

js 复制代码
canvas.on("mouse:wheel", function (opt) {
   onsole.log("opt", opt);
   const deltaY = opt.e.deltaY;
   const deltaX = opt.e.deltaX;
   zoom *= 0.999 ** deltaY;
   if (zoom > 20) zoom = 20;
   if (zoom < 0.01) zoom = 0.01;
   const vpt = this.viewportTransform;
   console.log("vpt", vpt);
   vpt[0] = zoom; // 在 X 轴方向上的偏移量。
   vpt[3] = zoom; // 在 Y 轴方向上的偏移量。
   vpt[4] += deltaX; // 俩个点的位置差
   vpt[5] += deltaY; // 俩个点的位置差
   this.requestRenderAll();
   this.setViewportTransform(this.viewportTransform);

   // let zoom = canvas.getZoom();
   // zoom *= 0.999 ** delta;
   // if (zoom > 20) zoom = 20;
   // if (zoom < 0.01) zoom = 0.01;
   // canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
   opt.e.preventDefault();
   opt.e.stopPropagation();
});

演示效果👇:

演示地址: enson0131.github.io/mini-fabric...

仓库案例

github.com/enson0131/m...

参考文档

相关推荐
计算机魔术师17 分钟前
管谷歌的不是Pichai一个人,是一整层AI智能体
前端
米米商聊21 分钟前
图标能点,按钮就可用了吗?键盘、名称与收藏状态
前端·javascript
Younglina1 小时前
三个晚上,我和 WorkBuddy 做了个生图网站
前端·后端·ai编程
xy34531 小时前
Axure 9.0 内联框架加载方式
前端·ui·html·axure·原型·产品设计
技术小甜甜2 小时前
【Cursor】Skill 被标黄色警告,怎么改成合格的 SKILL.md
开发语言·javascript·ai编程·cursor·1024程序员节·ai agent·skills
陈随易2 小时前
副业搞钱点子之收入查看器
前端·后端
Profile排查笔记2 小时前
浏览器指纹逆向怎么分析:从网络请求追踪到 JavaScript 采集链
前端·人工智能·后端·自动化
hudie7653 小时前
LikeShop 二开最常改的 5 个文件(附改前改后对比)
前端·uni-app·php·个人开发
qetfw3 小时前
Bash 函数与流程控制:read、if、for、case 验证
linux·前端·javascript·bash
鬼手点金4 小时前
从 API 消息到模型 Token:Chat Template
java·前端·javascript·数据库·人工智能·深度学习·1024程序员节