Fabric.js介绍 第六部分

了解转换如何在fabricJS上工作是尽可能顺利地编写应用程序的关键方面。

与转换相关的方法和属性

如果您计划理解和使用与自定义代码一起使用的fabricJS转换,那么这些方法应该是您最应该学习使用的方法。
一般来说,在本页面中,我们将矩阵称为6个数字的数组,表示平面上的转换,并将其作为一个类似{x: number, y: number}简单的JS对象或者是fabric.Point类的实例。(通常没有区别)

Canvas:
- vieportTransform = matrix;
Objects:
- matrix = fabric.Object.prototype.calcTransformMatrix();
- matrix = fabric.Object.prototype.calcOwnMatrix();
Utils:
- point = fabric.util.transformPoint(point, matrix);
- matrix = fabric.util.multiplyTransformMatrices(matrix, matrix);
- matrix = fabric.util.invertTransform(matrix);
- options = fabric.util.qrDecompose(matrix);

从一个空间移动到另一个空间(空间变换)

使用fabricJS常常需要与坐标和位置交互,但如果没有正确的背景,理解这些坐标的位置可能会很麻烦。
我将列出变换及其用法,然后我将尝试制作两个例子来更好地阐明发生了什么,以及如何做到这一点。

Canvas.viewportTransform,将虚拟画布的一点移动到缩放和平移空间。
在cooridantes可以找到一个点,当画布不缩放和不平移时,在viewportTransfrom M中应用缩放和平移后,在位置P处的位置可以找到:

newP = fabric.util.transformPoint(P, canvas.viewportTransform);

Object.calcTransformMatrix,将表示特定时刻的通用对象转换(受顶部、左侧、缩放和许多其他属性的影响)的矩阵还原,并将点从对象空间移动到画布空间,而不是缩放。因此,给定物体空间坐标中的一个点在坐标P处,这个点将在画布上的坐标为:

newP = fabric.util.transformPoint(P, object.calcTransformMatrix());

变形顺序

渲染期间的Fabric按此顺序应用转换:

zoom and pan => object transformation => nested object ( group ) => nested object deeper ( nested groups )

恢复顺序

invertTransform实用程序用于在转换逻辑中移回以进行一些反向计算:
假设您想要在画布上标记一个对象,单击鼠标,单击点。点击点P,例如元素上的10,10像素。对象被缩放和旋转,画布被缩放和平移。
要反转渲染计算,您可以遵循以下逻辑:

// 计算应用于对象像素的总转换:
var mCanvas = canvas.viewportTransform;
var mObject = object.calcTransformMatrix();
var mTotal = fabric.util.multiplyTransformMatrices(mCanvas, mObject); // 反转顺序会产生错误的结果
var mInverse = fabric.util.invertTransform(mTotal);
var pointInObjectPixels = fabric.util.transformPoint(pointClicked, mInverse);

现在,pointInObjectPixels是一个位于坐标空间中的点,其中0,0位于对象的中心。

了解矩阵的效果

给定top,left,angle,scaleX,scaleY,skewX,skewY,flipX,flipY相对简单,可以创建表示该转换的矩阵。 不直接的是如何回去。矩阵有6个维度,有6个数字,而属性是7,因为我们可以按比例缩放。确实存在无数个矩阵,但是可能的属性组合的数量是一个无限大的。
现在开始使用fabric.util.qrDecompose(matrix)可以为我们解码矩阵。给定函数的通用可逆矩阵,它返回包含这些信息的选项对象:

{
  angle: number, // 度
  scaleX: number,
  scaleY: number,
  skewX: number, // 度
  skewY: 0, // 总是0度
  translateX: number,
  translateY: number,
}

这个函数给出了这个矩阵的一个可能解,将skewY约束为0。

一个真实的用例

一个开发人员想要将对象分组,但同时让它们自由。理想情况下,当主要对象移动时,他希望其他一些对象跟随它。
为了解释这个例子,我将调用主要对象BOSS和其他MINIONS

假设画布周围有一些物体我们可以自由移动它们。在某一点上,我们要锁定它们的相对位置和比例尺,并移动一个。当我们设置我们想要的位置时,BOSS位置由矩阵描述,正如我们到目前为止所学到的,以及每个MINIONS。

我确信它存在一个矩阵,它定义了从BOSS移动到MINIONS的必要转换,我必须找到它。

// 我在寻找未知关系矩阵,其中:
BOSS * UNKNOW = MINION
// 我向左乘以BOSS-INV
BOSS-INV * BOSS * UNKNOW = BOSS-INV * MINION
// BOSS-INV * BOSS = IDENTIY, 一个中立的矩阵。
IDENTITY * UNKNOW = BOSS-INV * MINION
// so...
UNKNOW = BOSS-INV * MINION
// 在fabricJS代码中等于:
var minions = canvas.getObjects().filter(o => o !== boss);
var bossTransform = boss.calcTransformMatrix();
var invertedBossTransform = fabric.util.invertTransform(bossTransform);
minions.forEach(o => {
  var desiredTransform = multiply(invertedBossTransform, o.calcTransformMatrix());
  // 在这里保存所需的关系。
  o.relationship = desiredTransform;
});

好的,现在我知道如何找到关系,我可以编写一些事件处理程序来在每个BOSS操作上应用这种关系。
查看demo

var canvas = new fabric.Canvas('c');
var boss = new fabric.Rect(
  { width: 150, height: 200, fill: 'red' });
var minion1 = new fabric.Rect(
  { width: 40, height: 40, fill: 'blue' });
var minion2 = new fabric.Rect(
  { width: 40, height: 40, fill: 'blue' });

canvas.add(boss, minion1, minion2);

boss.on('moving', updateMinions);
boss.on('rotating', updateMinions);
boss.on('scaling', updateMinions);

var multiply = fabric.util.multiplyTransformMatrices;
var invert = fabric.util.invertTransform;

function updateMinions() {
  var minions = canvas.getObjects().filter(o => o !== boss);
  minions.forEach(o => {
    if (!o.relationship) {
      return;
    }
    var relationship = o.relationship;
    var newTransform = multiply(
      boss.calcTransformMatrix(),
      relationship
    );
    opt = fabric.util.qrDecompose(newTransform);
    o.set({
      flipX: false,
      flipY: false,
    });
    o.setPositionByOrigin(
      { x: opt.translateX, y: opt.translateY },
      'center',
      'center'
    );
    o.set(opt);
    o.setCoords();
  });
}

document.getElementById('bind').onclick = function() {
  var minions = canvas.getObjects().filter(o => o !== boss);
  var bossTransform = boss.calcTransformMatrix();
  var invertedBossTransform = invert(bossTransform);
  minions.forEach(o => {
    var desiredTransform = multiply(
      invertedBossTransform,
      o.calcTransformMatrix()
    );
    o.relationship = desiredTransform;
  });
}
    

下一章:Fabric.js介绍 第七部分

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354