学到了!Figma 原来是这样表示矩形的
2024-02-06 10:11:29 软件 319观看
摘要大家好,我是前端西瓜哥。今天我们来研究一下 Figma 是如何表示图形的,这里以矩形为切入点进行研究。明白最简单的矩形的表示后,研究其他的图形就可以举一反三。矩形的一般表达如果让我设计一个矩形图形的物理属性,我会怎

gZJ28资讯网——每日最新资讯28at.com

大家好,我是前端西瓜哥。gZJ28资讯网——每日最新资讯28at.com

今天我们来研究一下 Figma 是如何表示图形的,这里以矩形为切入点进行研究。gZJ28资讯网——每日最新资讯28at.com

明白最简单的矩形的表示后,研究其他的图形就可以举一反三。gZJ28资讯网——每日最新资讯28at.com

矩形的一般表达

如果让我设计一个矩形图形的物理属性,我会怎么设计?gZJ28资讯网——每日最新资讯28at.com

我张口就来:x、y、width、height、rotation。gZJ28资讯网——每日最新资讯28at.com

对一些简单的图形编辑操作,这些属性基本上是够用的,比如白板工具,如果你不考虑或者不希望图形可以翻转(flip) 的话。gZJ28资讯网——每日最新资讯28at.com

Figma 需要考虑翻转的情况的,此外还有斜切的情况。gZJ28资讯网——每日最新资讯28at.com

翻转的场景:gZJ28资讯网——每日最新资讯28at.com

gZJ28资讯网——每日最新资讯28at.com

还有斜切的场景,在选中多个图形然后缩放时有发生。gZJ28资讯网——每日最新资讯28at.com

gZJ28资讯网——每日最新资讯28at.com

这些表达光靠上面的几个属性是不够的,我们看看 Figma为了表达这些效果,是怎么去设计矩形的。gZJ28资讯网——每日最新资讯28at.com

Figma 矩形物理属性

与物理信息相关的属性如下:gZJ28资讯网——每日最新资讯28at.com

{  "size": {    "x": 100,    "y": 100  },  "transform": {    "m00": 1,    "m01": 3,    "m02": 5,    "m10": 2,    "m11": 4,    "m12": 6  },  // 省略其他无关属性}

没有位置属性,这个属性默认是 (0, 0),实际它转移到 transform 的矩阵的位移子矩阵上了。gZJ28资讯网——每日最新资讯28at.com

size 表示宽高,但属性名用的是 x(宽) 和 y(高),理论上 width 和 height 语义更好,这样应该是用了矢量类型。gZJ28资讯网——每日最新资讯28at.com

size 表示宽高,理论上 width 和 height 语义更好,这样应该是用了平面矢量类型的结构体,所以是 x 和 y。gZJ28资讯网——每日最新资讯28at.com

transform 表示一个 3x3 的变换矩阵。gZJ28资讯网——每日最新资讯28at.com

m00 | m01 | m02m10 | m11 | m12 0  |  0  |  1

上面的 transform 属性的值所对应的矩阵为:gZJ28资讯网——每日最新资讯28at.com

1 | 3 | 52 | 4 | 60 | 0 | 1

属性面板

再看看这些属性对应的右侧属性面板。gZJ28资讯网——每日最新资讯28at.com

gZJ28资讯网——每日最新资讯28at.com

x、y 分别是 5 和 6,它是 (0, 0) 进行 transform 后的结果,这个直接对应 transform.m02 和 tansfrom.m12。gZJ28资讯网——每日最新资讯28at.com

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 }// 或直接点const topLeft = { x: 5, y: 6 }

这里引入了 pixi.js 的 matrix 类,该类使用列向量方式进行表达。gZJ28资讯网——每日最新资讯28at.com

文末有 demo 源码以及线上 demo,可打开控制台查看结果验证正确性。gZJ28资讯网——每日最新资讯28at.com

然后这里的 width 和 height,是 223.61 和 500, 怎么来的?gZJ28资讯网——每日最新资讯28at.com

它们对应的是矩形的两条边变形后的长度,如下:gZJ28资讯网——每日最新资讯28at.com

gZJ28资讯网——每日最新资讯28at.com

uiWidth 为 (0, 0) 和 (width, 0)  进行矩阵变换后坐标点之间的距离。gZJ28资讯网——每日最新资讯28at.com

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

最后计算出 223.60679774997897,四舍五入得到 223.61。gZJ28资讯网——每日最新资讯28at.com

高度计算同理。gZJ28资讯网——每日最新资讯28at.com

uiHeight 为 (0, 0) 和 (0, height)  进行矩阵变换后坐标点之间的距离。gZJ28资讯网——每日最新资讯28at.com

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

旋转角度

最后是旋转角度,它是宽度对应的矩形边向量,逆时针旋转 90 度的向量所对应的角度。gZJ28资讯网——每日最新资讯28at.com

gZJ28资讯网——每日最新资讯28at.com

先计算宽边向量,然后逆时针旋转 90 度得到旋转向量,最后计算旋转向量对应的角度。gZJ28资讯网——每日最新资讯28at.com

const wSideVec = { x: topRight.x - topLeft.x, y: topRight.y - topLeft.y };// 逆时针旋转 90 度,得到旋转向量const rotationMatrix = new Matrix(0, -1, 1, 0, 0, 0);const rotationVec = rotationMatrix.apply(wSideVec);const rad = calcVectorRadian(rotationVec);const deg = rad2Deg(rad); //

这里用了几个工具函数。gZJ28资讯网——每日最新资讯28at.com

// 计算和 (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;}// 弧度转角度const rad2Deg = (rad) => (rad * 180) / Math.PI;

Figma 的角度表示比较别扭。gZJ28资讯网——每日最新资讯28at.com

特征为:基准角度朝上,对应向量为 (0, -1),角度方向为逆时针,角度范围限定为 (-180, 180],计算向量角度时要注意这个特征进行调整。gZJ28资讯网——每日最新资讯28at.com

gZJ28资讯网——每日最新资讯28at.com

完整代码实现

线上 demo:gZJ28资讯网——每日最新资讯28at.com

https://codepen.io/F-star/pen/WNPVWwQ?editors=0012。gZJ28资讯网——每日最新资讯28at.com

代码实现:gZJ28资讯网——每日最新资讯28at.com

import { Matrix } from "pixi.js";// 计算和 (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;}// 弧度转角度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 };  // 逆时针旋转 90 度,得到旋转向量  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,  });

运行一下,结果和属性面板一致。gZJ28资讯网——每日最新资讯28at.com

gZJ28资讯网——每日最新资讯28at.com

结尾

Figma 只用宽高和变换矩阵来表达矩形,在数据层可以用精简的数据表达丰富的变形,此外在渲染的时候也能将矩阵运算交给 GPU 进行并行运算,是不错的做法。gZJ28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-74664-0.html学到了!Figma 原来是这样表示矩形的

声明:本网页内容旨在传播知识,不代表本站观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。

显示全文

上一篇:接手外包团队开发的微服务项目,我感觉我的头快要裂开了

下一篇:.NET轻量级ORM框架Dapper.NET的高级应用实例详解

最新热点