【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 修改 viewportTransform[0]、viewportTransform[3] 达到缩放横纵坐标的值

2 修改 viewportTransform[4]、viewportTransform[5] 达到平移画布的效果

对于上面这个例子,我们只需要修改 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...

参考文档

相关推荐
Pee Wee9 分钟前
责任链模式
java·前端·责任链模式
我家媳妇儿萌哒哒27 分钟前
vue Element Ui Upload 上传 点击一个按钮,选择多个文件后直接上传,使用防抖解决多次上传的问题。
前端·javascript·vue.js
前端青山1 小时前
JavaScript闭包的深度剖析与实际应用
开发语言·前端·javascript·前端框架·ecmascript
JINGWHALE11 小时前
设计模式 结构型 组合模式(Composite Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·组合模式
青颜的天空1 小时前
CSS 中 content换行符实现打点 loading 正在加载中的效果
前端·css
DX_水位流量监测1 小时前
水库水位监测系统的自动化功能:减少人工干预,可实现实时监控
运维·前端·人工智能·自动化·制造·信息与通信·零售
m0_548503032 小时前
打包部署若依(RuoYi)SpringBoot后端和Vue前端图文教程
前端·vue.js·spring boot
阿芯爱编程2 小时前
清除数字栈
java·服务器·前端
GISer_Jing3 小时前
React函数组件中与生命周期相关Hooks详解
前端·javascript·react.js
要加油哦~3 小时前
尚硅谷· vue3+ts 知识点学习整理 |14h的课程(持续更ing)
javascript·vue.js·学习