@quinninc/pixi-transformer 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -61
- package/dist/FreeTransformTool.d.ts +6 -10
- package/dist/FreeTransformTool.js +61 -159
- package/dist/Transformer.d.ts +80 -0
- package/dist/Transformer.js +668 -0
- package/dist/graphics.d.ts +52 -0
- package/dist/graphics.js +115 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/vector.d.ts +1 -20
- package/dist/vector.js +6 -43
- package/package.json +41 -41
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
import { Pixi } from "./pixi";
|
|
2
|
+
import { calcAngleRadians, calcDistance, getDirection, rotatePoint, snapToEdgesAndCorners, } from "./graphics";
|
|
3
|
+
export class Transformer extends Pixi.Container {
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param p.lineColor - border line color
|
|
7
|
+
* @param p.handleColor - handles' fill color
|
|
8
|
+
* @param p.controlsSize - handles' size in pixels
|
|
9
|
+
* @param p.debug - show scaling distance debug lines
|
|
10
|
+
* @param p.generateAnchorMark - a function to generate your custom anchor mark
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const selectTool = new FreeTransformTool({
|
|
14
|
+
* lineColor: 0x66cfff,
|
|
15
|
+
* handleColor: 0xffffff,
|
|
16
|
+
* controlsSize: 10,
|
|
17
|
+
* debug: false,
|
|
18
|
+
* generateAnchorMark: (g) => g.clear().circle(0,0,10).fill(0xff0000); // please use clear() to clear the geometry first every time. leave undefined for default anchor mark. set false to disable.
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* root.addChild(selectTool); // by default the tool has a z-index of 1e7 (10000000). you can change it according to your requirement
|
|
22
|
+
*
|
|
23
|
+
* selectTool.select(obj);
|
|
24
|
+
*
|
|
25
|
+
* selectTool.unselect();
|
|
26
|
+
*/
|
|
27
|
+
constructor({ id = "transformer", lineColor = 0x66cfff, handleColor = 0xffffff, controlsSize = 10, debug = false, generateAnchorMark, } = {}) {
|
|
28
|
+
super();
|
|
29
|
+
// target Pixi.Container
|
|
30
|
+
this.target = null;
|
|
31
|
+
// position state
|
|
32
|
+
this.left = 0;
|
|
33
|
+
this.top = 0;
|
|
34
|
+
this.bottom = 0;
|
|
35
|
+
this.right = 0;
|
|
36
|
+
this._anchor = new Pixi.Point(0, 0);
|
|
37
|
+
this.dragging = false;
|
|
38
|
+
this.onUpdate = () => { };
|
|
39
|
+
const that = this;
|
|
40
|
+
this._id = id;
|
|
41
|
+
this.debug = debug;
|
|
42
|
+
this.lineColor = lineColor;
|
|
43
|
+
this.handleColor = handleColor;
|
|
44
|
+
this.handleOpacity = 1;
|
|
45
|
+
this.controlsSize = controlsSize;
|
|
46
|
+
this.controlsDimOpacity = 1;
|
|
47
|
+
this.controlsStrokeThickness = 2;
|
|
48
|
+
this.movedThreshold = 10;
|
|
49
|
+
if (typeof generateAnchorMark == "function")
|
|
50
|
+
this.generateAnchorMark = generateAnchorMark;
|
|
51
|
+
else if (generateAnchorMark === false)
|
|
52
|
+
this.generateAnchorMark = () => { };
|
|
53
|
+
this.zIndex = 1e7;
|
|
54
|
+
// Pixi.Container Properties
|
|
55
|
+
this.visible = false;
|
|
56
|
+
// create border
|
|
57
|
+
this.border = new Pixi.Graphics({ label: "Border" });
|
|
58
|
+
this.addChild(this.border);
|
|
59
|
+
// Anchor mark
|
|
60
|
+
this.anchorMark = new Pixi.Graphics({
|
|
61
|
+
label: "AnchorMark",
|
|
62
|
+
eventMode: "none",
|
|
63
|
+
});
|
|
64
|
+
this.anchorMark.alpha = this.handleOpacity;
|
|
65
|
+
this.addChild(this.anchorMark);
|
|
66
|
+
// debug indicators
|
|
67
|
+
this.fromPoint = new Pixi.Graphics({
|
|
68
|
+
label: "fromPoint",
|
|
69
|
+
alpha: this.handleOpacity * 0.75,
|
|
70
|
+
});
|
|
71
|
+
this.toPoint = new Pixi.Graphics({
|
|
72
|
+
label: "toPoint",
|
|
73
|
+
alpha: this.handleOpacity * 0.75,
|
|
74
|
+
});
|
|
75
|
+
this.addChild(this.fromPoint, this.toPoint);
|
|
76
|
+
// scaling handles
|
|
77
|
+
this.scaleTHandle = this.createHandle({
|
|
78
|
+
name: "scaleTHandle",
|
|
79
|
+
cursor: "n-resize",
|
|
80
|
+
});
|
|
81
|
+
this.scaleRHandle = this.createHandle({
|
|
82
|
+
name: "scaleRHandle",
|
|
83
|
+
cursor: "w-resize",
|
|
84
|
+
});
|
|
85
|
+
this.scaleBHandle = this.createHandle({
|
|
86
|
+
name: "scaleBHandle",
|
|
87
|
+
cursor: "s-resize",
|
|
88
|
+
});
|
|
89
|
+
this.scaleLHandle = this.createHandle({
|
|
90
|
+
name: "scaleLHandle",
|
|
91
|
+
cursor: "e-resize",
|
|
92
|
+
});
|
|
93
|
+
this.scaleTLHandle = this.createHandle({
|
|
94
|
+
name: "scaleTLHandle",
|
|
95
|
+
cursor: "nw-resize",
|
|
96
|
+
});
|
|
97
|
+
this.scaleTRHandle = this.createHandle({
|
|
98
|
+
name: "scaleTRHandle",
|
|
99
|
+
cursor: "ne-resize",
|
|
100
|
+
});
|
|
101
|
+
this.scaleBRHandle = this.createHandle({
|
|
102
|
+
name: "scaleBRHandle",
|
|
103
|
+
cursor: "se-resize",
|
|
104
|
+
});
|
|
105
|
+
this.scaleBLHandle = this.createHandle({
|
|
106
|
+
name: "scaleBLHandle",
|
|
107
|
+
cursor: "sw-resize",
|
|
108
|
+
});
|
|
109
|
+
// rotation handle
|
|
110
|
+
this.rotateHandle = this.createHandle({
|
|
111
|
+
name: "Rotate",
|
|
112
|
+
cursor: "pointer",
|
|
113
|
+
shape: "circle",
|
|
114
|
+
});
|
|
115
|
+
this.addChild(this.scaleTHandle, this.scaleRHandle, this.scaleBHandle, this.scaleLHandle, this.scaleTLHandle, this.scaleTRHandle, this.scaleBRHandle, this.scaleBLHandle, this.rotateHandle);
|
|
116
|
+
// ------------<MoveTool>---------------
|
|
117
|
+
this.moveHandle = new Pixi.Graphics({ label: this.id });
|
|
118
|
+
this.moveHandle.eventMode = "dynamic";
|
|
119
|
+
this.addToolTip(this.moveHandle, "Move", "move");
|
|
120
|
+
this.moveHandle
|
|
121
|
+
.on("pointerdown", onMoveHandleDown)
|
|
122
|
+
.on("pointerup", onMoveHandleUp)
|
|
123
|
+
.on("pointerupoutside", onMoveHandleUp);
|
|
124
|
+
this.handleHandleEvents(this.moveHandle, this);
|
|
125
|
+
this.moveHandle.hitArea = new Pixi.Rectangle(0, 0, this.controlsSize, this.controlsSize);
|
|
126
|
+
this.addChild(this.moveHandle);
|
|
127
|
+
function changeAnchorPoint(ev) {
|
|
128
|
+
// console.log("TAPP this, that - ", this, that);
|
|
129
|
+
if (!ev.ctrlKey && !ev.metaKey)
|
|
130
|
+
return;
|
|
131
|
+
if (!that.target)
|
|
132
|
+
return;
|
|
133
|
+
const eventPoint = that.convertToLocal(ev.global);
|
|
134
|
+
console.log("Prev anchor - ", that._anchor);
|
|
135
|
+
console.log("ev.position - ", eventPoint.x, eventPoint.y);
|
|
136
|
+
console.log("that.target.position - ", that.target.position.x, that.target.position.y);
|
|
137
|
+
console.log("that.target.pivot - ", that.target.pivot.x, that.target.pivot.y);
|
|
138
|
+
let dx, dy;
|
|
139
|
+
// calc new pivot point
|
|
140
|
+
// step 1 - translate to origin
|
|
141
|
+
dx = eventPoint.x - that.target.position.x;
|
|
142
|
+
dy = eventPoint.y - that.target.position.y;
|
|
143
|
+
// step 2 - rotate by -angle
|
|
144
|
+
const rp = rotatePoint({ x: dx, y: dy }, { x: 0, y: 0 }, -that.target.rotation);
|
|
145
|
+
let rdx = rp.x;
|
|
146
|
+
let rdy = rp.y;
|
|
147
|
+
const sp = { x: rdx, y: rdy };
|
|
148
|
+
const pSnapped = snapToEdgesAndCorners({
|
|
149
|
+
point: sp,
|
|
150
|
+
dim: { width: that.target.width, height: that.target.height },
|
|
151
|
+
anchor: that._anchor,
|
|
152
|
+
});
|
|
153
|
+
// console.log("UP SNAPPING - ", sp, pSnapped);
|
|
154
|
+
// step 3 - scale by 1/scale
|
|
155
|
+
rdx = pSnapped.x / that.target.scale.x;
|
|
156
|
+
rdy = pSnapped.y / that.target.scale.y;
|
|
157
|
+
// console.log("3 - dx, dy - ", dx, dy);
|
|
158
|
+
// step 4 - translate to new pivot
|
|
159
|
+
const x = that.target.pivot.x + rdx;
|
|
160
|
+
const y = that.target.pivot.y + rdy;
|
|
161
|
+
// calc new position
|
|
162
|
+
const rrp = rotatePoint({ x: rdx, y: rdy }, { x: 0, y: 0 }, that.target.rotation);
|
|
163
|
+
rrp.x = rrp.x * that.target.scale.x + that.target.position.x;
|
|
164
|
+
rrp.y = rrp.y * that.target.scale.y + that.target.position.y;
|
|
165
|
+
const posX = rrp.x;
|
|
166
|
+
const posY = rrp.y;
|
|
167
|
+
that.target.pivot.set(x, y);
|
|
168
|
+
that.target.position.set(posX, posY);
|
|
169
|
+
}
|
|
170
|
+
function onMoveHandleDown(downEvent) {
|
|
171
|
+
// console.log("onMoveHandleDown this, that - ", this, that);
|
|
172
|
+
if (!that.target)
|
|
173
|
+
return;
|
|
174
|
+
if (downEvent.ctrlKey || downEvent.metaKey) {
|
|
175
|
+
this.draggingPivot = true;
|
|
176
|
+
}
|
|
177
|
+
// data
|
|
178
|
+
this.targetStartPos = that.target.position.clone();
|
|
179
|
+
this.dragDistance = 0;
|
|
180
|
+
this.on("globalpointermove", onMoveHandleMove);
|
|
181
|
+
}
|
|
182
|
+
function onMoveHandleMove(moveEvent) {
|
|
183
|
+
// console.log("onMoveHandleMove this, that - ", this, that);
|
|
184
|
+
if (!this.dragging || !that.target)
|
|
185
|
+
return;
|
|
186
|
+
const eventPoint = that.convertToLocal(moveEvent.global);
|
|
187
|
+
if (this.draggingPivot) {
|
|
188
|
+
// changing pivot point
|
|
189
|
+
let x = eventPoint.x - that.target.x;
|
|
190
|
+
let y = eventPoint.y - that.target.y;
|
|
191
|
+
const p = rotatePoint({ x, y }, { x: 0, y: 0 }, -that.target.rotation);
|
|
192
|
+
const snappedPoint = snapToEdgesAndCorners({
|
|
193
|
+
point: p,
|
|
194
|
+
dim: {
|
|
195
|
+
width: that.target.width,
|
|
196
|
+
height: that.target.height,
|
|
197
|
+
},
|
|
198
|
+
anchor: that._anchor,
|
|
199
|
+
});
|
|
200
|
+
// console.log("MOVE SNAPPING - ", p, snappedPoint);
|
|
201
|
+
that.anchorMark.position.set(snappedPoint.x / that.target.scale.x, snappedPoint.y / that.target.scale.y);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const moveDelta = new Pixi.Point(eventPoint.x - this.eventStartPos.x, eventPoint.y - this.eventStartPos.y);
|
|
205
|
+
that.target.position.x = this.targetStartPos.x + moveDelta.x;
|
|
206
|
+
that.target.position.y = this.targetStartPos.y + moveDelta.y;
|
|
207
|
+
this.dragDistance = calcDistance(eventPoint, this.eventStartPos);
|
|
208
|
+
that.update();
|
|
209
|
+
moveEvent.stopPropagation();
|
|
210
|
+
}
|
|
211
|
+
function onMoveHandleUp(upEvent) {
|
|
212
|
+
// console.log("onMoveHandleUp this, that - ", this, that);
|
|
213
|
+
upEvent.stopPropagation();
|
|
214
|
+
if (this.draggingPivot) {
|
|
215
|
+
changeAnchorPoint(upEvent);
|
|
216
|
+
that.anchorMark.position.set(0, 0);
|
|
217
|
+
}
|
|
218
|
+
this.draggingPivot = false;
|
|
219
|
+
this.off("globalpointermove", onMoveHandleMove);
|
|
220
|
+
}
|
|
221
|
+
// ------------</ MoveTool>---------------
|
|
222
|
+
// ------------<ScaleXYTool>---------------
|
|
223
|
+
for (const handle of [
|
|
224
|
+
this.scaleBRHandle,
|
|
225
|
+
this.scaleTRHandle,
|
|
226
|
+
this.scaleBLHandle,
|
|
227
|
+
this.scaleTLHandle,
|
|
228
|
+
]) {
|
|
229
|
+
handle
|
|
230
|
+
.on("pointerdown", onScaleXYToolDown)
|
|
231
|
+
.on("pointerupoutside", onScaleXYToolUp)
|
|
232
|
+
.on("pointerup", onScaleXYToolUp);
|
|
233
|
+
}
|
|
234
|
+
function onScaleXYToolDown(downEvent) {
|
|
235
|
+
// console.log("onScaleToolDown this, that - ", this, that);
|
|
236
|
+
if (!that.target)
|
|
237
|
+
return;
|
|
238
|
+
this.targetResolutionStart = that.target.resolution;
|
|
239
|
+
this.on("globalpointermove", onScaleXYToolMove);
|
|
240
|
+
}
|
|
241
|
+
function onScaleXYToolMove(moveEvent) {
|
|
242
|
+
// console.log("onScaleToolMove this, that - ", this, that);
|
|
243
|
+
if (!this.dragging || !that.target)
|
|
244
|
+
return;
|
|
245
|
+
const eventPoint = that.convertToLocal(moveEvent.global);
|
|
246
|
+
const distStart = calcDistance(this.eventStartPos, that.target.position);
|
|
247
|
+
const distEnd = calcDistance(eventPoint, that.target.position);
|
|
248
|
+
const direction = getDirection(that.position, this.eventStartPos, eventPoint);
|
|
249
|
+
this.rescaleFactor = (distEnd / distStart) * direction;
|
|
250
|
+
that.target.scale.x = this.targetStartScale.x * this.rescaleFactor;
|
|
251
|
+
that.target.scale.y = this.targetStartScale.y * this.rescaleFactor;
|
|
252
|
+
that.update();
|
|
253
|
+
}
|
|
254
|
+
function onScaleXYToolUp() {
|
|
255
|
+
// console.log("onScaleToolUp this, that - ", this, that);
|
|
256
|
+
if (that.target) {
|
|
257
|
+
that.target.resolution =
|
|
258
|
+
this.targetResolutionStart * this.rescaleFactor;
|
|
259
|
+
}
|
|
260
|
+
this.off("globalpointermove", onScaleXYToolMove);
|
|
261
|
+
}
|
|
262
|
+
// ------------</ ScaleXYTool>---------------
|
|
263
|
+
// ------------<ScaleYTool>---------------
|
|
264
|
+
for (const handle of [this.scaleTHandle, this.scaleBHandle]) {
|
|
265
|
+
handle
|
|
266
|
+
.on("pointerdown", onScaleYToolDown)
|
|
267
|
+
.on("pointerup", onScaleYToolUp)
|
|
268
|
+
.on("pointerupoutside", onScaleYToolUp);
|
|
269
|
+
}
|
|
270
|
+
function onScaleYToolDown(downEvent) {
|
|
271
|
+
// console.log("onVScaleToolDown this, that - ", this, that);
|
|
272
|
+
if (!that.target)
|
|
273
|
+
return;
|
|
274
|
+
this.on("globalpointermove", onScaleYToolMove);
|
|
275
|
+
}
|
|
276
|
+
function onScaleYToolMove(moveEvent) {
|
|
277
|
+
// console.log("onVScaleToolMove this - ", this);
|
|
278
|
+
if (!this.dragging || !that.target)
|
|
279
|
+
return;
|
|
280
|
+
const eventPoint = that.convertToLocal(moveEvent.global);
|
|
281
|
+
const referencePoint = that.target.position.clone();
|
|
282
|
+
// referencePoint.x = that.target.position.x + that.target.width / 2;
|
|
283
|
+
const distStart = calcDistance(this.eventStartPos, referencePoint);
|
|
284
|
+
const distEnd = calcDistance(eventPoint, referencePoint);
|
|
285
|
+
const direction = getDirection(referencePoint, this.eventStartPos, eventPoint);
|
|
286
|
+
this.rescaleFactor = (distEnd / distStart) * direction;
|
|
287
|
+
that.target.scale.y = this.targetStartScale.y * this.rescaleFactor;
|
|
288
|
+
that.update();
|
|
289
|
+
}
|
|
290
|
+
function onScaleYToolUp(upEvent) {
|
|
291
|
+
// console.log("onVScaleToolUp this, that - ", this, that);
|
|
292
|
+
this.off("globalpointermove", onScaleYToolMove);
|
|
293
|
+
}
|
|
294
|
+
// ------------</ ScaleYTool>---------------
|
|
295
|
+
// ------------<ScaleXTool>---------------
|
|
296
|
+
for (const handle of [this.scaleRHandle, this.scaleLHandle]) {
|
|
297
|
+
handle
|
|
298
|
+
.on("pointerdown", onScaleXToolDown)
|
|
299
|
+
.on("pointerup", onScaleXToolUp)
|
|
300
|
+
.on("pointerupoutside", onScaleXToolUp);
|
|
301
|
+
}
|
|
302
|
+
this.addChild(this.scaleRHandle);
|
|
303
|
+
function onScaleXToolDown(downEvent) {
|
|
304
|
+
// console.log("onHScaleToolDown this, that - ", this, that);
|
|
305
|
+
if (!that.target)
|
|
306
|
+
return;
|
|
307
|
+
this.on("globalpointermove", onScaleXToolMove);
|
|
308
|
+
}
|
|
309
|
+
function onScaleXToolMove(moveEvent) {
|
|
310
|
+
// console.log("onHScaleToolMove this - ", this);
|
|
311
|
+
if (!this.dragging || !that.target)
|
|
312
|
+
return;
|
|
313
|
+
const eventPoint = that.convertToLocal(moveEvent.global);
|
|
314
|
+
const referencePoint = that.target.position.clone();
|
|
315
|
+
// referencePoint.y = that.target.position.y + that.target.height / 2;
|
|
316
|
+
const distStart = calcDistance(this.eventStartPos, referencePoint);
|
|
317
|
+
const distEnd = calcDistance(eventPoint, referencePoint);
|
|
318
|
+
const direction = getDirection(referencePoint, this.eventStartPos, eventPoint);
|
|
319
|
+
const rescaleFactor = (distEnd / distStart) * direction;
|
|
320
|
+
that.target.scale.x = this.targetStartScale.x * rescaleFactor;
|
|
321
|
+
that.update();
|
|
322
|
+
}
|
|
323
|
+
function onScaleXToolUp(upEvent) {
|
|
324
|
+
// console.log("onHScaleToolUp this, that - ", this, that);
|
|
325
|
+
this.off("globalpointermove", onScaleXToolMove);
|
|
326
|
+
}
|
|
327
|
+
// ------------</ ScaleXTool>---------------
|
|
328
|
+
// ------------<RotateTool>---------------
|
|
329
|
+
this.rotateHandle
|
|
330
|
+
.on("pointerdown", onRotateToolDown)
|
|
331
|
+
.on("pointerup", onRotateToolUp)
|
|
332
|
+
.on("pointerupoutside", onRotateToolUp);
|
|
333
|
+
function onRotateToolDown(downEvent) {
|
|
334
|
+
// console.log("onRotateToolDown this, that - ", this, that);
|
|
335
|
+
if (!that.target)
|
|
336
|
+
return;
|
|
337
|
+
this.on("globalpointermove", onRotateToolMove);
|
|
338
|
+
}
|
|
339
|
+
function onRotateToolMove(moveEvent) {
|
|
340
|
+
// console.log("onRotateToolMove this, that - ", this, that);
|
|
341
|
+
if (!this.dragging || !that.target)
|
|
342
|
+
return;
|
|
343
|
+
const eventPoint = that.convertToLocal(moveEvent.global);
|
|
344
|
+
// the drag point is relative to the display object x,y position on the stage (it's registration point)
|
|
345
|
+
const relativeStartPoint = {
|
|
346
|
+
x: this.eventStartPos.x - that.target.x,
|
|
347
|
+
y: this.eventStartPos.y - that.target.y,
|
|
348
|
+
};
|
|
349
|
+
const relativeEndPoint = {
|
|
350
|
+
x: eventPoint.x - that.target.x,
|
|
351
|
+
y: eventPoint.y - that.target.y,
|
|
352
|
+
};
|
|
353
|
+
const startAngle = calcAngleRadians(relativeStartPoint.x, relativeStartPoint.y);
|
|
354
|
+
const endAngle = calcAngleRadians(relativeEndPoint.x, relativeEndPoint.y);
|
|
355
|
+
const deltaAngle = endAngle - startAngle;
|
|
356
|
+
let finalRotation = this.targetStartRotation + deltaAngle;
|
|
357
|
+
if (moveEvent.ctrlKey || moveEvent.metaKey) {
|
|
358
|
+
const snapAngle = 45;
|
|
359
|
+
let finalAngle = (finalRotation * 180) / Math.PI;
|
|
360
|
+
const proximity = Math.min(Math.abs(finalAngle % snapAngle), snapAngle - Math.abs(finalAngle % snapAngle));
|
|
361
|
+
const snapProximity = 6;
|
|
362
|
+
if (proximity < snapProximity) {
|
|
363
|
+
finalAngle = Math.round(finalAngle / snapAngle) * snapAngle;
|
|
364
|
+
finalRotation = (finalAngle * Math.PI) / 180;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
that.target.rotation = finalRotation;
|
|
368
|
+
that.update();
|
|
369
|
+
}
|
|
370
|
+
function onRotateToolUp(upEvent) {
|
|
371
|
+
// console.log("onRotateToolUp this, that - ", this, that);
|
|
372
|
+
this.off("globalpointermove", onRotateToolMove);
|
|
373
|
+
}
|
|
374
|
+
// ------------</ RotateTool>---------------
|
|
375
|
+
}
|
|
376
|
+
addToolTip(shape, name, cursor) {
|
|
377
|
+
shape.on("pointerover", () => {
|
|
378
|
+
if (!this.dragging) {
|
|
379
|
+
this.setTitle(name);
|
|
380
|
+
this.setCursor(cursor);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
shape.on("pointerout", () => {
|
|
384
|
+
if (!this.dragging) {
|
|
385
|
+
this.setTitle("");
|
|
386
|
+
this.setCursor("default");
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
shape.on("pointerupoutside", () => {
|
|
390
|
+
this.setTitle("");
|
|
391
|
+
this.setCursor("default");
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
handleHandleEvents(handle, that) {
|
|
395
|
+
// console.log("handleHandleEvents this, handle - ", this, handle.label);
|
|
396
|
+
function onHandleDown(e) {
|
|
397
|
+
// console.log("onHandleDown this - ", this);
|
|
398
|
+
that.dragging = true;
|
|
399
|
+
handle.dragging = true;
|
|
400
|
+
handle.eventStartPos = that.convertToLocal(e.global.clone());
|
|
401
|
+
handle.targetStartScale = that.target.scale.clone();
|
|
402
|
+
handle.targetStartRotation = that.target.rotation;
|
|
403
|
+
handle.on("globalpointermove", onHandleMove);
|
|
404
|
+
// graphic - fromPoint
|
|
405
|
+
if (that.debug) {
|
|
406
|
+
that.createHandleIndicatorTo(that.fromPoint, handle.eventStartPos.x, handle.eventStartPos.y);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function onHandleMove(e) {
|
|
410
|
+
// console.log("onHandleMove this - ", this);
|
|
411
|
+
const eventPoint = that.convertToLocal(e.global.clone());
|
|
412
|
+
if (that.debug) {
|
|
413
|
+
that.createHandleIndicatorTo(that.toPoint, eventPoint.x, eventPoint.y);
|
|
414
|
+
that.deScale(that.fromPoint);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function onHandleUp() {
|
|
418
|
+
// console.log("onHandleUp this - ", this);
|
|
419
|
+
that.update();
|
|
420
|
+
handle.off("globalpointermove", onHandleMove);
|
|
421
|
+
if (that.debug) {
|
|
422
|
+
that.fromPoint.clear();
|
|
423
|
+
that.toPoint.clear();
|
|
424
|
+
}
|
|
425
|
+
handle.dragging = false;
|
|
426
|
+
that.dragging = false;
|
|
427
|
+
}
|
|
428
|
+
handle
|
|
429
|
+
.on("pointerdown", onHandleDown)
|
|
430
|
+
.on("pointerup", onHandleUp)
|
|
431
|
+
.on("pointerupoutside", onHandleUp);
|
|
432
|
+
}
|
|
433
|
+
createHandle({ cursor, name, shape = "square", }) {
|
|
434
|
+
const handle = new Pixi.Graphics();
|
|
435
|
+
handle.label = this.id;
|
|
436
|
+
handle.eventMode = "dynamic";
|
|
437
|
+
handle.alpha = this.handleOpacity;
|
|
438
|
+
this.addToolTip(handle, name, cursor);
|
|
439
|
+
if (shape === "circle") {
|
|
440
|
+
handle.ellipse(this.controlsSize / 2, this.controlsSize / 2, this.controlsSize / 2, this.controlsSize / 2);
|
|
441
|
+
}
|
|
442
|
+
else
|
|
443
|
+
handle.rect(0, 0, this.controlsSize, this.controlsSize);
|
|
444
|
+
handle
|
|
445
|
+
.stroke({ width: this.controlsStrokeThickness, color: this.lineColor })
|
|
446
|
+
.fill(this.handleColor);
|
|
447
|
+
handle.pivot.set(this.controlsSize / 2, this.controlsSize / 2);
|
|
448
|
+
this.handleHandleEvents(handle, this);
|
|
449
|
+
return handle;
|
|
450
|
+
}
|
|
451
|
+
createHandleIndicatorTo(handle, x, y) {
|
|
452
|
+
x = x - this.position.x;
|
|
453
|
+
y = y - this.position.y;
|
|
454
|
+
const p1 = new Pixi.Point(x, y);
|
|
455
|
+
const origin = new Pixi.Point(0, 0);
|
|
456
|
+
const p = rotatePoint(p1, origin, -this.rotation);
|
|
457
|
+
x = p.x;
|
|
458
|
+
y = p.y;
|
|
459
|
+
const size = this.controlsSize * 1.25;
|
|
460
|
+
handle
|
|
461
|
+
.clear()
|
|
462
|
+
.moveTo(0, 0)
|
|
463
|
+
.lineTo(x, y)
|
|
464
|
+
.stroke({ width: 5, color: 0xffffff })
|
|
465
|
+
.rect(x - size / 2, y - size / 2, size, size)
|
|
466
|
+
.fill(0xffffff);
|
|
467
|
+
this.deScale(handle);
|
|
468
|
+
}
|
|
469
|
+
deScale(target) {
|
|
470
|
+
target.scale.set(1 / this.scale.x, 1 / this.scale.y);
|
|
471
|
+
}
|
|
472
|
+
// public methods:
|
|
473
|
+
select(target) {
|
|
474
|
+
if (!target) {
|
|
475
|
+
this.unselect();
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
// console.log("select this - ", this);
|
|
479
|
+
this.target = target;
|
|
480
|
+
let _anchor;
|
|
481
|
+
if (this.target.pivot) {
|
|
482
|
+
// console.log("target has pivot", this.target.pivot);
|
|
483
|
+
// when you set a width of 100 on a sprites with image with actual width of 500, the sprite container is scaled down to 0.2 by default
|
|
484
|
+
_anchor = new Pixi.Point(this.target.pivot.x / Math.abs(this.target.width / this.target.scale.x), this.target.pivot.y /
|
|
485
|
+
Math.abs(this.target.height / this.target.scale.y));
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
// console.log("no-pivot|no-anchor", this.target.pivot);
|
|
489
|
+
_anchor = new Pixi.Point(0.5, 0.5);
|
|
490
|
+
}
|
|
491
|
+
this._anchor = _anchor;
|
|
492
|
+
if (this.parent !== this.target.parent) {
|
|
493
|
+
// console.log("Different parent - switching transformer location position");
|
|
494
|
+
this.target.parent.addChild(this);
|
|
495
|
+
}
|
|
496
|
+
// console.log("Anchor - ", _anchor);
|
|
497
|
+
this.update();
|
|
498
|
+
this.visible = true;
|
|
499
|
+
}
|
|
500
|
+
unselect() {
|
|
501
|
+
this.target = null;
|
|
502
|
+
this.visible = false;
|
|
503
|
+
this.onUpdate();
|
|
504
|
+
}
|
|
505
|
+
update() {
|
|
506
|
+
if (!this.target) {
|
|
507
|
+
console.log("no target, returning...");
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
this.onUpdate();
|
|
511
|
+
// copy object translation/transformation
|
|
512
|
+
const bounds = this.target.getLocalBounds();
|
|
513
|
+
this.width = bounds.width;
|
|
514
|
+
this.height = bounds.height;
|
|
515
|
+
this.scale.x = this.target.scale.x;
|
|
516
|
+
this.scale.y = this.target.scale.y;
|
|
517
|
+
this.x = this.target.x;
|
|
518
|
+
this.y = this.target.y;
|
|
519
|
+
this.rotation = this.target.rotation;
|
|
520
|
+
let _anchor;
|
|
521
|
+
if (this.target.pivot) {
|
|
522
|
+
// console.log("target has pivot", this.target.pivot);
|
|
523
|
+
_anchor = new Pixi.Point(this.target.pivot.x / Math.abs(this.target.width / this.scale.x), this.target.pivot.y / Math.abs(this.target.height / this.scale.y));
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
_anchor = this._anchor || new Pixi.Point(0.5, 0.5);
|
|
527
|
+
}
|
|
528
|
+
this._anchor = _anchor;
|
|
529
|
+
// console.log("Anchor - ", _anchor);
|
|
530
|
+
this.left = -bounds.width * _anchor.x;
|
|
531
|
+
this.top = -bounds.height * _anchor.y;
|
|
532
|
+
this.bottom = bounds.height * (1 - _anchor.y);
|
|
533
|
+
this.right = bounds.width * (1 - _anchor.x);
|
|
534
|
+
// anchor mark
|
|
535
|
+
this.generateAnchorMark?.(this.anchorMark);
|
|
536
|
+
this.deScale(this.anchorMark);
|
|
537
|
+
// this.anchorMark!.rotation = -this.rotation;
|
|
538
|
+
const rotationHandlePos = {
|
|
539
|
+
x: (this.left + this.right) / 2,
|
|
540
|
+
y: this.top - 80 / Math.abs(this.scale.y),
|
|
541
|
+
};
|
|
542
|
+
// borders
|
|
543
|
+
this.border
|
|
544
|
+
.clear()
|
|
545
|
+
.moveTo(this.left, this.top)
|
|
546
|
+
.lineTo(this.right, this.top)
|
|
547
|
+
.moveTo(this.left, this.bottom)
|
|
548
|
+
.lineTo(this.right, this.bottom)
|
|
549
|
+
.stroke({
|
|
550
|
+
width: this.controlsStrokeThickness / this.scale.y,
|
|
551
|
+
color: this.lineColor,
|
|
552
|
+
})
|
|
553
|
+
.moveTo(this.left, this.top)
|
|
554
|
+
.lineTo(this.left, this.bottom)
|
|
555
|
+
.moveTo(this.right, this.top)
|
|
556
|
+
.lineTo(this.right, this.bottom)
|
|
557
|
+
.stroke({
|
|
558
|
+
width: this.controlsStrokeThickness / this.scale.x,
|
|
559
|
+
color: this.lineColor,
|
|
560
|
+
})
|
|
561
|
+
.moveTo(rotationHandlePos.x, this.top)
|
|
562
|
+
.lineTo(rotationHandlePos.x, rotationHandlePos.y)
|
|
563
|
+
.stroke({
|
|
564
|
+
width: this.controlsStrokeThickness / this.scale.x,
|
|
565
|
+
color: this.lineColor,
|
|
566
|
+
});
|
|
567
|
+
// draw move hit area
|
|
568
|
+
let anchorRectPoints = [];
|
|
569
|
+
const isAnchorOutside = this._anchor.x < 0 ||
|
|
570
|
+
this._anchor.y < 0 ||
|
|
571
|
+
this._anchor.x > 1 ||
|
|
572
|
+
this._anchor.y > 1;
|
|
573
|
+
if (isAnchorOutside) {
|
|
574
|
+
anchorRectPoints = [
|
|
575
|
+
new Pixi.Point(-this.controlsSize, -this.controlsSize),
|
|
576
|
+
new Pixi.Point(-this.controlsSize, this.controlsSize),
|
|
577
|
+
new Pixi.Point(this.controlsSize, this.controlsSize),
|
|
578
|
+
new Pixi.Point(this.controlsSize, -this.controlsSize),
|
|
579
|
+
new Pixi.Point(-this.controlsSize, -this.controlsSize),
|
|
580
|
+
];
|
|
581
|
+
}
|
|
582
|
+
this.moveHandle.hitArea = new Pixi.Polygon(new Pixi.Point(this.left, this.top), new Pixi.Point(this.right, this.top), new Pixi.Point(this.right, this.bottom), new Pixi.Point(this.left, this.bottom), new Pixi.Point(this.left, this.top), ...anchorRectPoints);
|
|
583
|
+
this.scaleTLHandle.position.set(this.left, this.top);
|
|
584
|
+
this.scaleTRHandle.position.set(this.right, this.top);
|
|
585
|
+
this.scaleBRHandle.position.set(this.right, this.bottom);
|
|
586
|
+
this.scaleBLHandle.position.set(this.left, this.bottom);
|
|
587
|
+
this.scaleTHandle.position.set(this.left + bounds.width / 2, this.top);
|
|
588
|
+
this.scaleRHandle.position.set(this.right, this.top + bounds.height / 2);
|
|
589
|
+
this.scaleBHandle.position.set(this.left + bounds.width / 2, this.bottom);
|
|
590
|
+
this.scaleLHandle.position.set(this.left, this.top + bounds.height / 2);
|
|
591
|
+
this.rotateHandle.position.set(rotationHandlePos.x, rotationHandlePos.y);
|
|
592
|
+
const allHandles = [
|
|
593
|
+
this.scaleTLHandle,
|
|
594
|
+
this.scaleTRHandle,
|
|
595
|
+
this.scaleBRHandle,
|
|
596
|
+
this.scaleBLHandle,
|
|
597
|
+
this.scaleTHandle,
|
|
598
|
+
this.scaleRHandle,
|
|
599
|
+
this.scaleBHandle,
|
|
600
|
+
this.scaleLHandle,
|
|
601
|
+
this.rotateHandle,
|
|
602
|
+
];
|
|
603
|
+
// descale all handles
|
|
604
|
+
allHandles.forEach((handle) => {
|
|
605
|
+
this.deScale(handle);
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
setTitle(title) {
|
|
609
|
+
title = title || "";
|
|
610
|
+
this.accessibleTitle = title;
|
|
611
|
+
}
|
|
612
|
+
setCursor(cursor) {
|
|
613
|
+
const cursors = [
|
|
614
|
+
"e-resize",
|
|
615
|
+
"se-resize",
|
|
616
|
+
"s-resize",
|
|
617
|
+
"sw-resize",
|
|
618
|
+
"w-resize",
|
|
619
|
+
"nw-resize",
|
|
620
|
+
"n-resize",
|
|
621
|
+
"ne-resize",
|
|
622
|
+
];
|
|
623
|
+
const index = cursors.indexOf(cursor);
|
|
624
|
+
if (index >= 0) {
|
|
625
|
+
const angle = 45;
|
|
626
|
+
let rotation = this.target.angle;
|
|
627
|
+
rotation = rotation + angle / 2;
|
|
628
|
+
let newIndex = index + Math.floor(rotation / angle);
|
|
629
|
+
newIndex = newIndex % cursors.length;
|
|
630
|
+
document.body.style.cursor = cursors[newIndex];
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
document.body.style.cursor = cursor;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
generateAnchorMark(graphic) {
|
|
637
|
+
const scale = 2;
|
|
638
|
+
const anchorInnerRadius = this.controlsSize * 0.125 * scale;
|
|
639
|
+
const anchorOuterRadius = this.controlsSize * 0.625 * scale;
|
|
640
|
+
graphic
|
|
641
|
+
.clear()
|
|
642
|
+
.circle(0, 0, this.controlsSize * 0.375 * scale)
|
|
643
|
+
.moveTo(0, anchorInnerRadius)
|
|
644
|
+
.lineTo(0, anchorOuterRadius)
|
|
645
|
+
.moveTo(-anchorInnerRadius, 0)
|
|
646
|
+
.lineTo(-anchorOuterRadius, 0)
|
|
647
|
+
.moveTo(0, -anchorInnerRadius)
|
|
648
|
+
.lineTo(0, -anchorOuterRadius)
|
|
649
|
+
.moveTo(anchorInnerRadius, 0)
|
|
650
|
+
.lineTo(anchorOuterRadius, 0)
|
|
651
|
+
.fill(0xff0000)
|
|
652
|
+
.stroke(0x111111);
|
|
653
|
+
}
|
|
654
|
+
get _target() {
|
|
655
|
+
return this.target;
|
|
656
|
+
}
|
|
657
|
+
get id() {
|
|
658
|
+
return this._id;
|
|
659
|
+
}
|
|
660
|
+
cleanup() { }
|
|
661
|
+
convertToLocal(point) {
|
|
662
|
+
if (this.target) {
|
|
663
|
+
const p = this.target.parent.toLocal(point);
|
|
664
|
+
return p;
|
|
665
|
+
}
|
|
666
|
+
return point;
|
|
667
|
+
}
|
|
668
|
+
}
|