自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

學(xué)到了!Figma 原來(lái)是這樣表示矩形的

開發(fā) 前端
Figma 只用寬高和變換矩陣來(lái)表達(dá)矩形,在數(shù)據(jù)層可以用精簡(jiǎn)的數(shù)據(jù)表達(dá)豐富的變形,此外在渲染的時(shí)候也能將矩陣運(yùn)算交給 GPU 進(jìn)行并行運(yùn)算,是不錯(cuò)的做法。

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

今天我們來(lái)研究一下 Figma 是如何表示圖形的,這里以矩形為切入點(diǎn)進(jìn)行研究。

明白最簡(jiǎn)單的矩形的表示后,研究其他的圖形就可以舉一反三。

矩形的一般表達(dá)

如果讓我設(shè)計(jì)一個(gè)矩形圖形的物理屬性,我會(huì)怎么設(shè)計(jì)?

我張口就來(lái):x、y、width、height、rotation。

對(duì)一些簡(jiǎn)單的圖形編輯操作,這些屬性基本上是夠用的,比如白板工具,如果你不考慮或者不希望圖形可以翻轉(zhuǎn)(flip) 的話。

Figma 需要考慮翻轉(zhuǎn)的情況的,此外還有斜切的情況。

翻轉(zhuǎn)的場(chǎng)景:

還有斜切的場(chǎng)景,在選中多個(gè)圖形然后縮放時(shí)有發(fā)生。

這些表達(dá)光靠上面的幾個(gè)屬性是不夠的,我們看看 Figma為了表達(dá)這些效果,是怎么去設(shè)計(jì)矩形的。

Figma 矩形物理屬性

與物理信息相關(guān)的屬性如下:

{
  "size": {
    "x": 100,
    "y": 100
  },
  "transform": {
    "m00": 1,
    "m01": 3,
    "m02": 5,
    "m10": 2,
    "m11": 4,
    "m12": 6
  },
  // 省略其他無(wú)關(guān)屬性
}

沒(méi)有位置屬性,這個(gè)屬性默認(rèn)是 (0, 0),實(shí)際它轉(zhuǎn)移到 transform 的矩陣的位移子矩陣上了。

size 表示寬高,但屬性名用的是 x(寬) 和 y(高),理論上 width 和 height 語(yǔ)義更好,這樣應(yīng)該是用了矢量類型。

size 表示寬高,理論上 width 和 height 語(yǔ)義更好,這樣應(yīng)該是用了平面矢量類型的結(jié)構(gòu)體,所以是 x 和 y。

transform 表示一個(gè) 3x3 的變換矩陣。

m00 | m01 | m02
m10 | m11 | m12
 0  |  0  |  1

上面的 transform 屬性的值所對(duì)應(yīng)的矩陣為:

1 | 3 | 5
2 | 4 | 6
0 | 0 | 1

屬性面板

再看看這些屬性對(duì)應(yīng)的右側(cè)屬性面板。

x、y 分別是 5 和 6,它是 (0, 0) 進(jìn)行 transform 后的結(jié)果,這個(gè)直接對(duì)應(yīng) transform.m02 和 tansfrom.m12。

import { Matrix } from "pixi.js";

const matrix = new Matrix(1, 2, 3, 4, 5, 6);
const topLeft = matrix.apply({ x: 0, y: 0 }); // { x: 5, y: 6 }

// 或直接點(diǎn)
const topLeft = { x: 5, y: 6 }

這里引入了 pixi.js 的 matrix 類,該類使用列向量方式進(jìn)行表達(dá)。

文末有 demo 源碼以及線上 demo,可打開控制臺(tái)查看結(jié)果驗(yàn)證正確性。

然后這里的 width 和 height,是 223.61 和 500, 怎么來(lái)的?

它們對(duì)應(yīng)的是矩形的兩條邊變形后的長(zhǎng)度,如下:

uiWidth 為 (0, 0) 和 (width, 0)  進(jìn)行矩陣變換后坐標(biāo)點(diǎn)之間的距離。

const distance = (p1, p2) => {
  const a = p1.x - p2.x;
  const b = p1.y - p2.y;
  return Math.sqrt(a * a + b * b);
};

const matrix = new Matrix(1, 2, 3, 4, 5, 6);
const topLeft = { x: 5, y: 6 }

const topRight = matrix.apply({ x: 100, y: 0 });
distance(topRight, topLeft); // 223.60679774997897

最后計(jì)算出 223.60679774997897,四舍五入得到 223.61。

高度計(jì)算同理。

uiHeight 為 (0, 0) 和 (0, height)  進(jìn)行矩陣變換后坐標(biāo)點(diǎn)之間的距離。

const matrix = new Matrix(1, 2, 3, 4, 5, 6);
const topLeft = { x: 5, y: 6 }

const bottomLeft = matrix.apply({ x: 0, y: 100 });
distance(bottomLeft, topLeft); // 500

旋轉(zhuǎn)角度

最后是旋轉(zhuǎn)角度,它是寬度對(duì)應(yīng)的矩形邊向量,逆時(shí)針旋轉(zhuǎn) 90 度的向量所對(duì)應(yīng)的角度。

先計(jì)算寬邊向量,然后逆時(shí)針旋轉(zhuǎn) 90 度得到旋轉(zhuǎn)向量,最后計(jì)算旋轉(zhuǎn)向量對(duì)應(yīng)的角度。

const wSideVec = { x: topRight.x - topLeft.x, y: topRight.y - topLeft.y };
// 逆時(shí)針旋轉(zhuǎn) 90 度,得到旋轉(zhuǎn)向量
const rotationMatrix = new Matrix(0, -1, 1, 0, 0, 0);
const rotationVec = rotationMatrix.apply(wSideVec);
const rad = calcVectorRadian(rotationVec);
const deg = rad2Deg(rad); //

這里用了幾個(gè)工具函數(shù)。

// 計(jì)算和 (0, -1) 的夾角
const calcVectorRadian = (vec) => {
  const a = [vec.x, vec.y];
  const b = [0, -1]; // 這個(gè)是基準(zhǔn)角度

  // 使用點(diǎn)積公式計(jì)算夾腳
  const dotProduct = a[0] * b[0] + a[1] * b[1];
  const d =
    Math.sqrt(a[0] * a[0] + a[1] * a[1]) * Math.sqrt(b[0] * b[0] + b[1] * b[1]);
  let rad = Math.acos(dotProduct / d);

  if (vec.x > 0) {
    // 如果 x > 0, 則 rad 轉(zhuǎn)為 (-PI, 0) 之間的值
    rad = -rad;
  }
  return rad;
}

// 弧度轉(zhuǎn)角度
const rad2Deg = (rad) => (rad * 180) / Math.PI;

Figma 的角度表示比較別扭。

特征為:基準(zhǔn)角度朝上,對(duì)應(yīng)向量為 (0, -1),角度方向?yàn)槟鏁r(shí)針,角度范圍限定為 (-180, 180],計(jì)算向量角度時(shí)要注意這個(gè)特征進(jìn)行調(diào)整。

完整代碼實(shí)現(xiàn)

線上 demo:

https://codepen.io/F-star/pen/WNPVWwQ?editors=0012。

代碼實(shí)現(xiàn):

import { Matrix } from "pixi.js";

// 計(jì)算和 (0, -1) 的夾角
const calcVectorRadian = (vec) => {
  const a = [vec.x, vec.y];
  const b = [0, -1];

  const dotProduct = a[0] * b[0] + a[1] * b[1];
  const d =
    Math.sqrt(a[0] * a[0] + a[1] * a[1]) * Math.sqrt(b[0] * b[0] + b[1] * b[1]);
  let rad = Math.acos(dotProduct / d);

  if (vec.x > 0) {
    // 如果 x > 0, 則 rad 為 (-PI, 0) 之間的值
    rad = -rad;
  }
  return rad;
}

// 弧度轉(zhuǎn)角度
const rad2Deg = (rad) => (rad * 180) / Math.PI;

const distance = (p1, p2) => {
  const a = p1.x - p2.x;
  const b = p1.y - p2.y;
  return Math.sqrt(a * a + b * b);
};

const getAttrs = (size, transform) => {
  const width = size.x;
  const height = size.y;
  const matrix = new Matrix(
    transform.m00, // 1
    transform.m10, // 2
    transform.m01, // 3
    transform.m11, // 4
    transform.m02, // 5
    transform.m12 // 6
  );

  const topLeft = { x: transform.m02, y: transform.m12 };
  console.log("x:", topLeft.x)
  console.log("y:", topLeft.y)

  const topRight = matrix.apply({ x: width, y: 0 });
  console.log("width:", distance(topRight, topLeft)); // 223.60679774997897

  const bottomLeft = matrix.apply({ x: 0, y: height });
  console.log("height:", distance(bottomLeft, topLeft)); // 500

  const wSideVec = { x: topRight.x - topLeft.x, y: topRight.y - topLeft.y };
  // 逆時(shí)針旋轉(zhuǎn) 90 度,得到旋轉(zhuǎn)向量
  const rotationMatrix = new Matrix(0, -1, 1, 0, 0, 0);
  const rotationVec = rotationMatrix.apply(wSideVec);

  const rad = calcVectorRadian(rotationVec);
  const deg = rad2Deg(rad);
  console.log("rotation:", deg); // -63.43494882292201
};

getAttrs(
  // 寬高
  { x: 100, y: 100 },
  // 變換矩陣
  {
    m00: 1,
    m01: 3,
    m02: 5,
    m10: 2,
    m11: 4,
    m12: 6,
  }
);

運(yùn)行一下,結(jié)果和屬性面板一致。

結(jié)尾

Figma 只用寬高和變換矩陣來(lái)表達(dá)矩形,在數(shù)據(jù)層可以用精簡(jiǎn)的數(shù)據(jù)表達(dá)豐富的變形,此外在渲染的時(shí)候也能將矩陣運(yùn)算交給 GPU 進(jìn)行并行運(yùn)算,是不錯(cuò)的做法。

責(zé)任編輯:姜華 來(lái)源: 前端西瓜哥
相關(guān)推薦

2024-04-30 08:22:51

Figma圖形編輯變換矩陣

2022-12-14 07:32:40

InnoDBMySQL引擎

2022-05-05 08:55:12

工業(yè)物聯(lián)網(wǎng)IIoT

2023-05-22 15:58:11

2018-04-02 15:13:21

網(wǎng)絡(luò)

2023-02-15 08:17:38

2025-02-17 09:22:16

MySQLSQL語(yǔ)句

2020-05-26 08:52:36

Java JVM多態(tài)

2020-11-24 06:20:02

Linux日志文件系統(tǒng)

2016-10-12 08:54:24

2021-08-17 07:00:00

雙重檢查鎖Nacos

2014-07-21 10:32:52

蘋果公司實(shí)習(xí)

2017-11-02 13:05:12

PC3D NAND內(nèi)存

2020-03-23 08:30:12

程序員男友感受

2018-10-26 10:41:19

2015-03-25 09:55:34

程序員程序員修補(bǔ)BUG真正原因

2022-07-13 10:37:59

服務(wù)器故障優(yōu)化

2017-05-09 15:39:33

ensorFlow機(jī)器人機(jī)器學(xué)習(xí)

2017-06-06 15:13:07

2011-05-25 09:58:46

C#
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)