-
-
Notifications
You must be signed in to change notification settings - Fork 159
/
Copy pathmain.js
204 lines (179 loc) · 5.52 KB
/
main.js
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/**
* @file LinearGradientFillの角度をget/setする
* @version 1.0.1
* @author sttk3.com
* @copyright (c) 2019 sttk3.com
*/
let panel ;
/**
* 指定したアイテムの線形グラデーションの角度を取得する
* @param {Scenenode} targetItem 対象のnode
* @return {Number} 角度(ラジアン)
*/
function getGradientAngle(targetItem) {
// 縦横比を取得する
const ratio = getHVRatio(targetItem) ;
/*
線形グラデーションからはgetEndPoints()で制御点の座標 [0: startX, 1: startY, 2: endX, 3: endY] が取れる。
これはアイテムの幅・高さをそれぞれ1とした比率で表しているため,正方形でないと角度が正しくならない。
比率から縦横比を考慮した座標に変換し,その角度を取得する
*/
const points = targetItem.fill.getEndPoints() ;
const res = Math.atan2((points[3] - points[1]) * -1, (points[2] - points[0]) * ratio) ;
return res ;
}
/**
* 指定したアイテムの線形グラデーションの角度をセットする
* @param {Scenenode} targetItem 対象のnode
* @param {Number} dstAngle 傾き角度(ラジアン)
* @return なし
*/
function setGradientAngle(targetItem, dstAngle) {
const oldAngle = getGradientAngle(targetItem) ;
const rotationAngle = oldAngle - radians(dstAngle) ;
const ratio = getHVRatio(targetItem) ;
// 比率から縦横比を考慮した座標に変換し,目標の角度まで回転した後に比率に戻す
let targetColor = targetItem.fill ;
const points = targetColor.getEndPoints() ; // [0: startX, 1: startY, 2: endX, 3: endY]
const centerAnchor = [0.5 * ratio, 0.5] ;
const newStartAnchor = rotateAnchor([centerAnchor[0], centerAnchor[1]], [points[0] * ratio, points[1]], rotationAngle) ;
const newEndAnchor = rotateAnchor([centerAnchor[0], centerAnchor[1]], [points[2] * ratio, points[3]], rotationAngle) ;
const ratio2 = 1 / ratio ;
targetColor.setEndPoints(newStartAnchor[0] * ratio2, newStartAnchor[1], newEndAnchor[0] * ratio2, newEndAnchor[1]) ;
// グラデーションを再セットする
targetItem.fill = targetColor ;
}
/**
* 指定したアイテムの 幅/高さ 比率を算出する
* @param {Scenenode} targetItem 対象のnode
* @return {Number}
*/
function getHVRatio(targetItem) {
const bounds = targetItem.boundsInParent ;
const res = bounds.width / bounds.height ;
return res ;
}
/**
* 座標を回転した時の新しい座標を取得する
* @param {Array} origin 中心点座標
* @param {Array} p1 回転対象座標
* @param {Number} angle 回転角度(ラジアン)
* @return {Array} [x, y]
*/
function rotateAnchor(origin, p1, angle) {
var vx = p1[0] - origin[0] ;
var vy = p1[1] - origin[1] ;
var cosNum = Math.cos(angle) ;
var sinNum = Math.sin(angle) ;
var x = vx * cosNum - vy * sinNum ;
var y = vx * sinNum + vy * cosNum ;
return [origin[0] + x, origin[1] + y] ;
}
/**
* ラジアンから角度に変換
* @param {Number} rad 角度(ラジアン)
* @return {Number} 角度(度)
*/
function degrees(rad) {
return rad * 180 / Math.PI ;
}
/**
* 角度からラジアンに変換
* @param {Number} deg 角度(度)
* @return {Number} 角度(ラジアン)
*/
function radians(deg) {
return deg * Math.PI / 180 ;
}
/**
* パネルを生成する
* @return {Panel}
*/
function create() {
const html = `
<style>
.break {
flex-wrap: wrap;
}
label.row > span {
color: #8E8E8E;
width: 16px;
text-align: right;
font-size: 9px;
}
label.row input {
flex: 1 1 auto;
}
form {
width:100%;
margin: 0px;
padding: 0px;
}
.show {
display: block;
}
.hide {
display: none;
}
</style>
<form method="dialog" id="main">
<div class="row break">
<label class="row">
<span>angle</span>
<input type="number" uxp-quiet="true" id="txtO" value="-90" placeholder="offset" />
</label>
</div>
</form>
<p id="warning">This plugin requires you to select a object with linear gradients in the document.</p>
`;
/**
* パネルのフォーム送信時実行するアクション
*/
function panelAction() {
const dstAngle = Number(document.querySelector('#txtO').value) ;
require('application').editDocument({editLabel: 'Set gradient angle'}, function() {
const {selection} = require('scenegraph') ;
const targetItem = selection.items[0] ;
setGradientAngle(targetItem, dstAngle) ;
}) ;
}
panel = document.createElement('div') ;
panel.innerHTML = html ;
panel.querySelector('form').addEventListener('submit', panelAction) ;
return panel ;
}
/**
* パネルを表示する
*/
function show(event) {
if(!panel) event.node.appendChild(create()) ;
}
/**
* パネルを隠す
*/
function hide(event) {}
/**
* 選択変更時パネルをアップデートする
*/
function update(selection) {
const form = document.querySelector("form") ;
const warning = document.querySelector("#warning") ;
if(selection && selection.items[0] && selection.items[0].fill.constructor.name == 'LinearGradientFill') {
let oldAngle = degrees(getGradientAngle(selection.items[0])).toFixed(3).replace(/\.0+$/, '') ;
document.querySelector('#txtO').value = oldAngle ;
form.className = 'show' ;
warning.className = 'hide' ;
} else {
form.className = 'hide' ;
warning.className = 'show' ;
}
}
module.exports = {
panels: {
gradientAngle: {
show,
hide,
update
}
}
} ;