-
Notifications
You must be signed in to change notification settings - Fork 317
/
Copy pathcode.ts
129 lines (105 loc) · 4.22 KB
/
code.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
// Circular text sample code
// Turns a selected text node into a set of letters on a circular arc.
// Combines two transforms by doing a matrix multiplication.
// The first transform applied is a, followed by b, which
// is normally written b * a.
function multiply(a, b) {
return [
[ a[0][0] * b[0][0] + a[0][1] * b[1][0], a[0][0] * b[0][1] + a[0][1] * b[1][1], a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] ],
[ a[1][0] * b[0][0] + a[1][1] * b[1][0], a[1][0] * b[0][1] + a[1][1] * b[1][1] + 0, a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] ]
]
}
// Creates a "move" transform.
function move(x, y) {
return [
[1, 0, x],
[0, 1, y]
]
}
// Creates a "rotate" transform.
function rotate(theta: number) {
return [
[Math.cos(theta), Math.sin(theta), 0],
[-Math.sin(theta), Math.cos(theta), 0]
]
}
// MAIN PLUGIN CODE
async function main(): Promise<string | undefined> {
// Inter Regular is the font that objects will be created with by default in
// Figma. We need to wait for fonts to load before creating text using them.
await figma.loadFontAsync({ family: "Inter", style: "Regular" })
// Make sure the selection is a single piece of text before proceeding.
if (figma.currentPage.selection.length !== 1) {
return "Select a single node."
}
const node = figma.currentPage.selection[0]
if (node.type !== 'TEXT') {
return "Select a single text node."
}
// Replace spaces with nonbreaking spaces.
const text = node.characters.replace(/ /g, " ")
const gap = 5
// Create a new text node for each character, and
// measure the total width.
const nodes = []
let width = 0
for (let i = 0; i < text.length; i++) {
const letterNode = figma.createText()
letterNode.fontSize = node.fontSize
letterNode.fontName = node.fontName
letterNode.characters = text.charAt(i)
width += letterNode.width
if (i !== 0) {
width += gap
}
node.parent.appendChild(letterNode)
nodes.push(letterNode)
}
// Make the radius half the width of the original text, minus a bit.
const r = node.width / 2 - 30
const pi = 3.1415926
// The arclength should be equal to the total desired width of the text,
// => theta * r = width
// => theta = width / r
//
// We define this angle such that 0 means pointing to the right, and pi/2 means
// pointing straight up.
//
// Using these conventions, the starting angle for our curved text is
// pi/2 + theta/2, and the ending angle is pi/2 - theta/2.
let angle = pi / 2 + width / (2*r)
const gapAngle = gap / r
const centerX = node.x + node.width / 2
const centerY = node.y + node.height / 2
// Walk through each letter and position it on a circle of radius r.
nodes.forEach(function (letterNode: TextNode) {
const stepAngle = letterNode.width / r
// Move forward in our arc half a letter width.
angle -= stepAngle / 2
const width = letterNode.width
const height = letterNode.height
// Move the letter so that the center of its baseline is on the origin.
// (estimate the baseline as being 70% down from the top of the box).
//
// We accomplish this by moving the letter so its top left is at (0, 0),
// then moving the letter to the left and up by the appopriate amount.
letterNode.x = 0
letterNode.y = 0
letterNode.relativeTransform = multiply(move(-width/2, -0.7 * height), letterNode.relativeTransform) as Transform
// Rotate the letter. Because we want to have the rotation angle be 0 at the top of the circle,
// we need to subtract pi/2 before applying the rotation to the text.
letterNode.relativeTransform = multiply(rotate(angle - pi/2), letterNode.relativeTransform) as Transform
// Move the letter to its position on the arc.
const desiredX = centerX + r * Math.cos(angle)
const desiredY = centerY - r * Math.sin(angle)
letterNode.relativeTransform = multiply(move(desiredX, desiredY), letterNode.relativeTransform) as Transform
// Move forward in our arc half a letter width + the gap
angle -= stepAngle / 2 + gapAngle
})
// Put all nodes in a group!
figma.group(nodes, node.parent)
}
void main().then((message: string | undefined) => {
figma.closePlugin(message)
})