@quinninc/pixi-transformer 0.0.6 → 0.0.7

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 CHANGED
@@ -1,61 +1,63 @@
1
- # Free Transform Tool for PIXI.js
2
-
3
- Works with pixi.js v8 and above.
4
-
5
- ![preview](https://cdn.quinn.live/transformer-image.png)
6
-
7
- ### ✨ Features
8
-
9
- - Translate - Drag to move anywhere
10
- - Scale - Scale on X, Y or both axis'
11
- - Rotate - drag to rotate
12
- - Anchor point - update anchor point of transforms (Ctrl + Click)
13
- - Snapping - hold ctrl to snap rotation at 45degrees, snap anchor point to edges and corners.
14
- - Theming - custom border color, control size and color, and anchor point graphic
15
-
16
- > Note - all transformations are applied wrt the pivot point, update pivot point to achieve the desired result.
17
-
18
- ## Installation
19
-
20
- ```bash
21
- npm install @quinninc/pixi-transformer
22
- ```
23
-
24
- ## Usage
25
-
26
- ```tsx
27
- const root = new Container();
28
- app.stage.addChild(root);
29
-
30
- // create tool and add to stage
31
- const selectTool = new FreeTransformTool();
32
- selectTool.label = "transform-tool";
33
- root.addChild(selectTool);
34
-
35
- const obj = new Sprite({
36
- texture: Texture.from("lenna"),
37
- label: "lenna",
38
- eventMode: "static",
39
- });
40
-
41
- root.addChild(obj);
42
-
43
- obj.on("pointertap", (ev) => {
44
- console.log("select - ", ev.currentTarget.label);
45
- selectTool.select(ev.currentTarget);
46
- });
47
- ```
48
-
49
- ## API
50
-
51
- ### `FreeTransformTool` (class)
52
-
53
- **Constructor Parameter Object:**
54
-
55
- - `lineColor` - border line color. default - `0x66cfff`
56
- - `handleColor` - (`number`) - handles' fill color. default - `0xffffff`
57
- - `controlsSize` - (`number`) - handles' size in pixels. default - `10`
58
- - `debug` - (`boolean`) - show scaling distance debug lines. default - `false`
59
- - `generateAnchorMark` - a function to generate your custom anchor mark. default - `undefined` (uses default anchor mark)
60
-
61
- **returns:** `PIXI.Container` (the select tool)
1
+ # Free Transform Tool for PIXI.js
2
+
3
+ Works with pixi.js v8 and above.
4
+
5
+ ![preview](https://cdn.quinn.live/transformer-image.png)
6
+
7
+ ### ✨ Features
8
+
9
+ - Translate - Drag to move anywhere
10
+ - Scale - Scale on X, Y or both axis'
11
+ - Rotate - drag to rotate
12
+ - Anchor point - update anchor point of transforms (AltLeft + Click or Drag)
13
+ - Snapping - snap rotation at 45degrees, snap anchor point to edges and corners (hold ShiftLeft).
14
+ - Theming - custom border color, control size and color, and anchor point graphic
15
+
16
+ > Note - all transformations are applied wrt the pivot point, update pivot point to achieve the desired result.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @quinninc/pixi-transformer
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```tsx
27
+ const root = new Container();
28
+ app.stage.addChild(root);
29
+
30
+ // create tool and add to stage
31
+ const selectTool = new FreeTransformTool();
32
+ selectTool.label = "transform-tool";
33
+ root.addChild(selectTool);
34
+
35
+ const obj = new Sprite({
36
+ texture: Texture.from("lenna"),
37
+ label: "lenna",
38
+ eventMode: "static",
39
+ });
40
+
41
+ root.addChild(obj);
42
+
43
+ obj.on("pointertap", (ev) => {
44
+ console.log("select - ", ev.currentTarget.label);
45
+ selectTool.select(ev.currentTarget);
46
+ });
47
+ ```
48
+
49
+ ## API
50
+
51
+ ### `FreeTransformTool` (class)
52
+
53
+ **Constructor Parameter Object:**
54
+
55
+ - `lineColor` - border line color. default - `0x66cfff`
56
+ - `handleColor` - (`number`) - handles' fill color. default - `0xffffff`
57
+ - `controlsSize` - (`number`) - handles' size in pixels. default - `10`
58
+ - `debug` - (`boolean`) - show scaling distance debug lines. default - `false`
59
+ - `generateAnchorMark` - a function to generate your custom anchor mark. default - `undefined` (uses default anchor mark)
60
+ - `pivotKeys` - (`string[]`) - keys to hold to change pivot point. default - `['AltLeft']` - [List of KeyCodes](https://www.freecodecamp.org/news/javascript-keycode-list-keypress-event-key-codes#a-full-list-of-key-event-values)
61
+ - `snapKeys` - (`string[]`) - keys to hold to snap to grid. default - `['ShiftLeft']`
62
+
63
+ **returns:** `PIXI.Container` (the select tool)
@@ -30,12 +30,16 @@ export declare class Transformer extends Pixi.Container {
30
30
  private right;
31
31
  private _anchor;
32
32
  private dragging;
33
+ private snapKey;
34
+ private pivotKey;
33
35
  /**
34
36
  *
35
37
  * @param p.lineColor - border line color
36
38
  * @param p.handleColor - handles' fill color
37
39
  * @param p.controlsSize - handles' size in pixels
38
40
  * @param p.debug - show scaling distance debug lines
41
+ * @param p.snapKeys - key to hold to enable snapping rotation to 45 degrees // default ['ShiftLeft']
42
+ * @param p.pivotKeys - key to hold to move the pivot point of transformation // default ['AltLeft']
39
43
  * @param p.generateAnchorMark - a function to generate your custom anchor mark
40
44
  *
41
45
  * @example
@@ -44,6 +48,8 @@ export declare class Transformer extends Pixi.Container {
44
48
  * handleColor: 0xffffff,
45
49
  * controlsSize: 10,
46
50
  * debug: false,
51
+ * snapKeys: ['ShiftLeft'],
52
+ * pivotKeys: ['AltLeft'],
47
53
  * 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.
48
54
  * });
49
55
  *
@@ -53,16 +59,19 @@ export declare class Transformer extends Pixi.Container {
53
59
  *
54
60
  * selectTool.unselect();
55
61
  */
56
- constructor({ id, lineColor, handleColor, controlsSize, debug, generateAnchorMark, }?: {
62
+ constructor({ id, lineColor, handleColor, controlsSize, debug, generateAnchorMark, snapKeys, pivotKeys, }?: {
57
63
  id?: string;
58
64
  lineColor?: string | number;
59
65
  handleColor?: string | number;
60
66
  controlsSize?: number;
61
67
  debug?: boolean;
62
68
  generateAnchorMark?: false | ((graphic: Pixi.Graphics) => void);
69
+ snapKeys?: string[];
70
+ pivotKeys?: string[];
63
71
  });
64
72
  private addToolTip;
65
73
  private handleHandleEvents;
74
+ private handleKeyPresses;
66
75
  private createHandle;
67
76
  private createHandleIndicatorTo;
68
77
  private deScale;
@@ -7,6 +7,8 @@ export class Transformer extends Pixi.Container {
7
7
  * @param p.handleColor - handles' fill color
8
8
  * @param p.controlsSize - handles' size in pixels
9
9
  * @param p.debug - show scaling distance debug lines
10
+ * @param p.snapKeys - key to hold to enable snapping rotation to 45 degrees // default ['ShiftLeft']
11
+ * @param p.pivotKeys - key to hold to move the pivot point of transformation // default ['AltLeft']
10
12
  * @param p.generateAnchorMark - a function to generate your custom anchor mark
11
13
  *
12
14
  * @example
@@ -15,6 +17,8 @@ export class Transformer extends Pixi.Container {
15
17
  * handleColor: 0xffffff,
16
18
  * controlsSize: 10,
17
19
  * debug: false,
20
+ * snapKeys: ['ShiftLeft'],
21
+ * pivotKeys: ['AltLeft'],
18
22
  * 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
23
  * });
20
24
  *
@@ -24,7 +28,7 @@ export class Transformer extends Pixi.Container {
24
28
  *
25
29
  * selectTool.unselect();
26
30
  */
27
- constructor({ id = "transformer", lineColor = 0x66cfff, handleColor = 0xffffff, controlsSize = 10, debug = false, generateAnchorMark, } = {}) {
31
+ constructor({ id = "transformer", lineColor = 0x66cfff, handleColor = 0xffffff, controlsSize = 10, debug = false, generateAnchorMark, snapKeys = ["ShiftLeft"], pivotKeys = ["AltLeft"], } = {}) {
28
32
  super();
29
33
  // target Pixi.Container
30
34
  this.target = null;
@@ -35,6 +39,8 @@ export class Transformer extends Pixi.Container {
35
39
  this.right = 0;
36
40
  this._anchor = new Pixi.Point(0, 0);
37
41
  this.dragging = false;
42
+ this.snapKey = false;
43
+ this.pivotKey = false;
38
44
  this.onUpdate = () => { };
39
45
  const that = this;
40
46
  this._id = id;
@@ -53,6 +59,9 @@ export class Transformer extends Pixi.Container {
53
59
  this.zIndex = 1e7;
54
60
  // Pixi.Container Properties
55
61
  this.visible = false;
62
+ // keyPress states
63
+ this.handleKeyPresses("snapKey", snapKeys, that);
64
+ this.handleKeyPresses("pivotKey", pivotKeys, that);
56
65
  // create border
57
66
  this.border = new Pixi.Graphics({ label: "Border" });
58
67
  this.addChild(this.border);
@@ -126,7 +135,7 @@ export class Transformer extends Pixi.Container {
126
135
  this.addChild(this.moveHandle);
127
136
  function changeAnchorPoint(ev) {
128
137
  // console.log("TAPP this, that - ", this, that);
129
- if (!ev.ctrlKey && !ev.metaKey)
138
+ if (!that.pivotKey)
130
139
  return;
131
140
  if (!that.target)
132
141
  return;
@@ -171,7 +180,7 @@ export class Transformer extends Pixi.Container {
171
180
  // console.log("onMoveHandleDown this, that - ", this, that);
172
181
  if (!that.target)
173
182
  return;
174
- if (downEvent.ctrlKey || downEvent.metaKey) {
183
+ if (that.pivotKey) {
175
184
  this.draggingPivot = true;
176
185
  }
177
186
  // data
@@ -354,7 +363,7 @@ export class Transformer extends Pixi.Container {
354
363
  const endAngle = calcAngleRadians(relativeEndPoint.x, relativeEndPoint.y);
355
364
  const deltaAngle = endAngle - startAngle;
356
365
  let finalRotation = this.targetStartRotation + deltaAngle;
357
- if (moveEvent.ctrlKey || moveEvent.metaKey) {
366
+ if (that.snapKey) {
358
367
  const snapAngle = 45;
359
368
  let finalAngle = (finalRotation * 180) / Math.PI;
360
369
  const proximity = Math.min(Math.abs(finalAngle % snapAngle), snapAngle - Math.abs(finalAngle % snapAngle));
@@ -430,6 +439,17 @@ export class Transformer extends Pixi.Container {
430
439
  .on("pointerup", onHandleUp)
431
440
  .on("pointerupoutside", onHandleUp);
432
441
  }
442
+ handleKeyPresses(property, keyCodes, that) {
443
+ typeof window > "u" ||
444
+ (window.addEventListener("keydown", (e) => {
445
+ keyCodes.includes(e.code) && (that[property] = true);
446
+ keyCodes.includes(e.key) && (that[property] = true);
447
+ }),
448
+ window.addEventListener("keyup", (e) => {
449
+ keyCodes.includes(e.code) && (that[property] = false);
450
+ keyCodes.includes(e.key) && (that[property] = false);
451
+ }));
452
+ }
433
453
  createHandle({ cursor, name, shape = "square", }) {
434
454
  const handle = new Pixi.Graphics();
435
455
  handle.label = this.id;
package/package.json CHANGED
@@ -1,41 +1,41 @@
1
- {
2
- "name": "@quinninc/pixi-transformer",
3
- "private": false,
4
- "version": "0.0.6",
5
- "repository": {
6
- "url": "https://github.com/Quinn-Care-Private-Limited/pixi-transformer"
7
- },
8
- "type": "module",
9
- "main": "dist/index.js",
10
- "module": "dist/index.js",
11
- "types": "dist/index.d.ts",
12
- "files": [
13
- "dist",
14
- "package.json",
15
- "README.md"
16
- ],
17
- "keywords": [
18
- "pixi.js",
19
- "transformer",
20
- "pixi.js v8"
21
- ],
22
- "author": {
23
- "name": "Rohit Kaushal",
24
- "url": "https://github.com/RohitKaushal7"
25
- },
26
- "license": "MIT",
27
- "scripts": {
28
- "dev": "tsc -w",
29
- "build": "tsc",
30
- "deploy": "npm run build && npm version patch && npm publish --access public"
31
- },
32
- "devDependencies": {
33
- "typescript": "^5.2.2",
34
- "vite": "^5.3.1",
35
- "pixi.js": "^8.2.1"
36
- },
37
- "dependencies": {},
38
- "peerDependencies": {
39
- "pixi.js": "^8.2.1"
40
- }
41
- }
1
+ {
2
+ "name": "@quinninc/pixi-transformer",
3
+ "private": false,
4
+ "version": "0.0.7",
5
+ "repository": {
6
+ "url": "https://github.com/Quinn-Care-Private-Limited/pixi-transformer"
7
+ },
8
+ "type": "module",
9
+ "main": "dist/index.js",
10
+ "module": "dist/index.js",
11
+ "types": "dist/index.d.ts",
12
+ "files": [
13
+ "dist",
14
+ "package.json",
15
+ "README.md"
16
+ ],
17
+ "keywords": [
18
+ "pixi.js",
19
+ "transformer",
20
+ "pixi.js v8"
21
+ ],
22
+ "author": {
23
+ "name": "Rohit Kaushal",
24
+ "url": "https://github.com/RohitKaushal7"
25
+ },
26
+ "license": "MIT",
27
+ "scripts": {
28
+ "dev": "tsc -w",
29
+ "build": "tsc",
30
+ "deploy": "npm run build && npm version patch && npm publish --access public"
31
+ },
32
+ "devDependencies": {
33
+ "typescript": "^5.2.2",
34
+ "vite": "^5.3.1",
35
+ "pixi.js": "^8.2.1"
36
+ },
37
+ "dependencies": {},
38
+ "peerDependencies": {
39
+ "pixi.js": "^8.2.1"
40
+ }
41
+ }
@@ -1,74 +0,0 @@
1
- import { Container, Graphics, Sprite } from "pixi.js";
2
- export declare class FreeTransformTool extends Container {
3
- private debug;
4
- private moveHandle;
5
- private rotateHandle;
6
- private scaleTHandle;
7
- private scaleRHandle;
8
- private scaleBHandle;
9
- private scaleLHandle;
10
- private scaleTLHandle;
11
- private scaleTRHandle;
12
- private scaleBRHandle;
13
- private scaleBLHandle;
14
- private border;
15
- private anchorMark;
16
- private fromPoint;
17
- private toPoint;
18
- private target;
19
- private movedThreshold;
20
- private handleOpacity;
21
- private controlsSize;
22
- private controlsDimOpacity;
23
- private controlsStrokeThickness;
24
- private lineColor;
25
- private handleColor;
26
- private left;
27
- private top;
28
- private bottom;
29
- private right;
30
- private dragging;
31
- /**
32
- *
33
- * @param p.lineColor - border line color
34
- * @param p.handleColor - handles' fill color
35
- * @param p.controlsSize - handles' size in pixels
36
- * @param p.debug - show scaling distance debug lines
37
- * @param p.generateAnchorMark - a function to generate your custom anchor mark
38
- *
39
- * @example
40
- * const selectTool = new FreeTransformTool({
41
- * lineColor: 0x66cfff,
42
- * handleColor: 0xffffff,
43
- * controlsSize: 10,
44
- * debug: false,
45
- * 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.
46
- * });
47
- *
48
- * root.addChild(selectTool); // by default the tool has a z-index of 1e7 (10000000). you can change it according to your requirement
49
- *
50
- * selectTool.select(obj);
51
- *
52
- * selectTool.unselect();
53
- */
54
- constructor({ lineColor, handleColor, controlsSize, debug, generateAnchorMark, }?: {
55
- lineColor?: string | number;
56
- handleColor?: string | number;
57
- controlsSize?: number;
58
- debug?: boolean;
59
- generateAnchorMark?: false | ((graphic: Graphics) => void);
60
- });
61
- private addToolTip;
62
- private handleHandleEvents;
63
- private createHandle;
64
- private createHandleIndicatorTo;
65
- private deScale;
66
- select(target: Container): void;
67
- unselect(): void;
68
- update(): void;
69
- setTitle(title: string): void;
70
- setCursor(cursor: string): void;
71
- private generateAnchorMark;
72
- get _target(): Container<import("pixi.js").ContainerChild> | Sprite | null;
73
- cleanup(): void;
74
- }
@@ -1,549 +0,0 @@
1
- import { Container, Graphics, Point, Rectangle, } from "pixi.js";
2
- import { calcAngleRadians, calcDistance, getDirection, rotatePoint, } from "./vector";
3
- export class FreeTransformTool extends 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({ lineColor = 0x66cfff, handleColor = 0xffffff, controlsSize = 10, debug = false, generateAnchorMark, } = {}) {
28
- super();
29
- // target 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.dragging = false;
37
- const that = this;
38
- this.debug = debug;
39
- this.lineColor = lineColor;
40
- this.handleColor = handleColor;
41
- this.handleOpacity = 1;
42
- this.controlsSize = controlsSize;
43
- this.controlsDimOpacity = 1;
44
- this.controlsStrokeThickness = 2;
45
- this.movedThreshold = 10;
46
- if (typeof generateAnchorMark == "function")
47
- this.generateAnchorMark = generateAnchorMark;
48
- else if (generateAnchorMark === false)
49
- this.generateAnchorMark = () => { };
50
- this.zIndex = 1e7;
51
- // Container Properties
52
- this.visible = false;
53
- // create border
54
- this.border = new Graphics({ label: "Border" });
55
- this.addChild(this.border);
56
- // Anchor mark
57
- this.anchorMark = new Graphics({ label: "AnchorMark" });
58
- this.anchorMark.alpha = this.handleOpacity;
59
- this.addChild(this.anchorMark);
60
- // debug indicators
61
- this.fromPoint = new Graphics({
62
- label: "fromPoint",
63
- alpha: this.handleOpacity * 0.75,
64
- });
65
- this.toPoint = new Graphics({
66
- label: "toPoint",
67
- alpha: this.handleOpacity * 0.75,
68
- });
69
- this.addChild(this.fromPoint, this.toPoint);
70
- // scaling handles
71
- this.scaleTHandle = this.createHandle({
72
- name: "scaleTHandle",
73
- cursor: "n-resize",
74
- });
75
- this.scaleRHandle = this.createHandle({
76
- name: "scaleRHandle",
77
- cursor: "w-resize",
78
- });
79
- this.scaleBHandle = this.createHandle({
80
- name: "scaleBHandle",
81
- cursor: "s-resize",
82
- });
83
- this.scaleLHandle = this.createHandle({
84
- name: "scaleLHandle",
85
- cursor: "e-resize",
86
- });
87
- this.scaleTLHandle = this.createHandle({
88
- name: "scaleTLHandle",
89
- cursor: "nw-resize",
90
- });
91
- this.scaleTRHandle = this.createHandle({
92
- name: "scaleTRHandle",
93
- cursor: "ne-resize",
94
- });
95
- this.scaleBRHandle = this.createHandle({
96
- name: "scaleBRHandle",
97
- cursor: "se-resize",
98
- });
99
- this.scaleBLHandle = this.createHandle({
100
- name: "scaleBLHandle",
101
- cursor: "sw-resize",
102
- });
103
- // rotation handle
104
- this.rotateHandle = this.createHandle({
105
- name: "Rotate",
106
- cursor: "pointer",
107
- shape: "circle",
108
- });
109
- this.addChild(this.scaleTHandle, this.scaleRHandle, this.scaleBHandle, this.scaleLHandle, this.scaleTLHandle, this.scaleTRHandle, this.scaleBRHandle, this.scaleBLHandle, this.rotateHandle);
110
- // ------------<MoveTool>---------------
111
- this.moveHandle = new Graphics({ label: "moveHandle" });
112
- this.moveHandle.interactive = true;
113
- this.addToolTip(this.moveHandle, "Move", "move");
114
- this.moveHandle
115
- .on("pointerdown", onMoveHandleDown)
116
- .on("pointerup", onMoveHandleUp)
117
- .on("pointerupoutside", onMoveHandleUp);
118
- this.handleHandleEvents(this.moveHandle, this);
119
- this.moveHandle.hitArea = new Rectangle(0, 0, this.controlsSize, this.controlsSize);
120
- this.addChild(this.moveHandle);
121
- function onMoveHandleDown(downEvent) {
122
- // console.log("onMoveHandleDown this, that - ", this, that);
123
- if (!that.target)
124
- return;
125
- // data
126
- this.targetStartPos = that.target.position.clone();
127
- this.dragDistance = 0;
128
- this.on("globalpointermove", onMoveHandleMove);
129
- }
130
- function onMoveHandleMove(moveEvent) {
131
- // console.log("onMoveHandleMove this, that - ", this, that);
132
- if (!this.dragging || !that.target)
133
- return;
134
- const moveDelta = new Point(moveEvent.global.x - this.eventStartGlobalPos.x, moveEvent.global.y - this.eventStartGlobalPos.y);
135
- that.target.position.x = this.targetStartPos.x + moveDelta.x;
136
- that.target.position.y = this.targetStartPos.y + moveDelta.y;
137
- this.dragDistance = calcDistance(moveEvent.global, this.eventStartGlobalPos);
138
- that.update();
139
- moveEvent.stopPropagation();
140
- }
141
- function onMoveHandleUp(upEvent) {
142
- // console.log("onMoveHandleUp this, that - ", this, that);
143
- upEvent.stopPropagation();
144
- // only deselect if there was very little movement on click
145
- // which helps on mobile devices, where it's difficult to
146
- // tap without dragging slightly
147
- if (!this.dragDistance || this.dragDistance < that.movedThreshold) {
148
- that.unselect();
149
- }
150
- this.off("globalpointermove", onMoveHandleMove);
151
- }
152
- // ------------</ MoveTool>---------------
153
- // ------------<ScaleXYTool>---------------
154
- for (const handle of [
155
- this.scaleBRHandle,
156
- this.scaleTRHandle,
157
- this.scaleBLHandle,
158
- this.scaleTLHandle,
159
- ]) {
160
- handle
161
- .on("pointerdown", onScaleXYToolDown)
162
- .on("pointerupoutside", onScaleXYToolUp)
163
- .on("pointerup", onScaleXYToolUp);
164
- }
165
- function onScaleXYToolDown(downEvent) {
166
- // console.log("onScaleToolDown this, that - ", this, that);
167
- if (!that.target)
168
- return;
169
- this.targetResolutionStart = that.target.resolution;
170
- this.on("globalpointermove", onScaleXYToolMove);
171
- }
172
- function onScaleXYToolMove(moveEvent) {
173
- // console.log("onScaleToolMove this, that - ", this, that);
174
- if (!this.dragging || !that.target)
175
- return;
176
- const distStart = calcDistance(this.eventStartGlobalPos, that.target.position);
177
- const distEnd = calcDistance(moveEvent.global, that.target.position);
178
- const direction = getDirection(new Point(that.position.x, that.position.y), new Point(this.eventStartGlobalPos.x, this.eventStartGlobalPos.y), new Point(moveEvent.global.x, moveEvent.global.y));
179
- this.rescaleFactor = (distEnd / distStart) * direction;
180
- that.target.scale.x = this.targetStartScale.x * this.rescaleFactor;
181
- that.target.scale.y = this.targetStartScale.y * this.rescaleFactor;
182
- that.update();
183
- }
184
- function onScaleXYToolUp() {
185
- // console.log("onScaleToolUp this, that - ", this, that);
186
- if (that.target) {
187
- that.target.resolution =
188
- this.targetResolutionStart * this.rescaleFactor;
189
- }
190
- this.off("globalpointermove", onScaleXYToolMove);
191
- }
192
- // ------------</ ScaleXYTool>---------------
193
- // ------------<ScaleYTool>---------------
194
- for (const handle of [this.scaleTHandle, this.scaleBHandle]) {
195
- handle
196
- .on("pointerdown", onScaleYToolDown)
197
- .on("pointerup", onScaleYToolUp)
198
- .on("pointerupoutside", onScaleYToolUp);
199
- }
200
- function onScaleYToolDown(downEvent) {
201
- // console.log("onVScaleToolDown this, that - ", this, that);
202
- if (!that.target)
203
- return;
204
- this.on("globalpointermove", onScaleYToolMove);
205
- }
206
- function onScaleYToolMove(moveEvent) {
207
- // console.log("onVScaleToolMove this - ", this);
208
- if (!this.dragging || !that.target)
209
- return;
210
- const referencePoint = that.target.position.clone();
211
- // referencePoint.x = that.target.position.x + that.target.width / 2;
212
- const distStart = calcDistance(this.eventStartGlobalPos, referencePoint);
213
- const distEnd = calcDistance(moveEvent.global, referencePoint);
214
- const direction = getDirection(referencePoint, this.eventStartGlobalPos, moveEvent.global);
215
- this.rescaleFactor = (distEnd / distStart) * direction;
216
- that.target.scale.y = this.targetStartScale.y * this.rescaleFactor;
217
- that.update();
218
- }
219
- function onScaleYToolUp(upEvent) {
220
- // console.log("onVScaleToolUp this, that - ", this, that);
221
- this.off("globalpointermove", onScaleYToolMove);
222
- }
223
- // ------------</ ScaleYTool>---------------
224
- // ------------<ScaleXTool>---------------
225
- for (const handle of [this.scaleRHandle, this.scaleLHandle]) {
226
- handle
227
- .on("pointerdown", onScaleXToolDown)
228
- .on("pointerup", onScaleXToolUp)
229
- .on("pointerupoutside", onScaleXToolUp);
230
- }
231
- this.addChild(this.scaleRHandle);
232
- function onScaleXToolDown(downEvent) {
233
- // console.log("onHScaleToolDown this, that - ", this, that);
234
- if (!that.target)
235
- return;
236
- this.on("globalpointermove", onScaleXToolMove);
237
- }
238
- function onScaleXToolMove(moveEvent) {
239
- // console.log("onHScaleToolMove this - ", this);
240
- if (!this.dragging || !that.target)
241
- return;
242
- const referencePoint = that.target.position.clone();
243
- // referencePoint.y = that.target.position.y + that.target.height / 2;
244
- const distStart = calcDistance(this.eventStartGlobalPos, referencePoint);
245
- const distEnd = calcDistance(moveEvent.global, referencePoint);
246
- const direction = getDirection(referencePoint, this.eventStartGlobalPos, moveEvent.global);
247
- const rescaleFactor = (distEnd / distStart) * direction;
248
- that.target.scale.x = this.targetStartScale.x * rescaleFactor;
249
- that.update();
250
- }
251
- function onScaleXToolUp(upEvent) {
252
- // console.log("onHScaleToolUp this, that - ", this, that);
253
- this.off("globalpointermove", onScaleXToolMove);
254
- }
255
- // ------------</ ScaleXTool>---------------
256
- // ------------<RotateTool>---------------
257
- this.rotateHandle
258
- .on("pointerdown", onRotateToolDown)
259
- .on("pointerup", onRotateToolUp)
260
- .on("pointerupoutside", onRotateToolUp);
261
- function onRotateToolDown(downEvent) {
262
- // console.log("onRotateToolDown this, that - ", this, that);
263
- if (!that.target)
264
- return;
265
- this.on("globalpointermove", onRotateToolMove);
266
- }
267
- function onRotateToolMove(moveEvent) {
268
- // console.log("onRotateToolMove this, that - ", this, that);
269
- if (!this.dragging || !that.target)
270
- return;
271
- // the drag point is relative to the display object x,y position on the stage (it's registration point)
272
- const relativeStartPoint = {
273
- x: this.eventStartGlobalPos.x - that.target.x,
274
- y: this.eventStartGlobalPos.y - that.target.y,
275
- };
276
- const relativeEndPoint = {
277
- x: moveEvent.global.x - that.target.x,
278
- y: moveEvent.global.y - that.target.y,
279
- };
280
- const startAngle = calcAngleRadians(relativeStartPoint.x, relativeStartPoint.y);
281
- const endAngle = calcAngleRadians(relativeEndPoint.x, relativeEndPoint.y);
282
- const deltaAngle = endAngle - startAngle;
283
- let finalRotation = this.targetStartRotation + deltaAngle;
284
- if (moveEvent.ctrlKey) {
285
- const snapAngle = 45;
286
- let finalAngle = (finalRotation * 180) / Math.PI;
287
- const proximity = Math.min(Math.abs(finalAngle % snapAngle), snapAngle - Math.abs(finalAngle % snapAngle));
288
- const snapProximity = 6;
289
- if (proximity < snapProximity) {
290
- finalAngle = Math.round(finalAngle / snapAngle) * snapAngle;
291
- finalRotation = (finalAngle * Math.PI) / 180;
292
- }
293
- }
294
- that.target.rotation = finalRotation;
295
- that.update();
296
- }
297
- function onRotateToolUp(upEvent) {
298
- // console.log("onRotateToolUp this, that - ", this, that);
299
- this.off("globalpointermove", onRotateToolMove);
300
- }
301
- // ------------</ RotateTool>---------------
302
- }
303
- addToolTip(shape, name, cursor) {
304
- shape.on("pointerover", () => {
305
- if (!this.dragging) {
306
- this.setTitle(name);
307
- this.setCursor(cursor);
308
- }
309
- });
310
- shape.on("pointerout", () => {
311
- if (!this.dragging) {
312
- this.setTitle("");
313
- this.setCursor("default");
314
- }
315
- });
316
- shape.on("pointerupoutside", () => {
317
- this.setTitle("");
318
- this.setCursor("default");
319
- });
320
- }
321
- handleHandleEvents(handle, that) {
322
- // console.log("handleHandleEvents this, handle - ", this, handle.label);
323
- function onHandleDown(e) {
324
- // console.log("onHandleDown this - ", this);
325
- that.dragging = true;
326
- handle.dragging = true;
327
- handle.eventStartGlobalPos = e.global.clone();
328
- handle.targetStartScale = that.target.scale.clone();
329
- handle.targetStartRotation = that.target.rotation;
330
- handle.on("globalpointermove", onHandleMove);
331
- // graphic - fromPoint
332
- if (that.debug) {
333
- that.createHandleIndicatorTo(that.fromPoint, handle.eventStartGlobalPos.x, handle.eventStartGlobalPos.y);
334
- }
335
- }
336
- function onHandleMove(e) {
337
- // console.log("onHandleMove this - ", this);
338
- if (that.debug) {
339
- that.createHandleIndicatorTo(that.toPoint, e.globalX, e.globalY);
340
- that.deScale(that.fromPoint);
341
- }
342
- }
343
- function onHandleUp() {
344
- // console.log("onHandleUp this - ", this);
345
- that.update();
346
- handle.off("globalpointermove", onHandleMove);
347
- if (that.debug) {
348
- that.fromPoint.clear();
349
- that.toPoint.clear();
350
- }
351
- handle.dragging = false;
352
- that.dragging = false;
353
- }
354
- handle
355
- .on("pointerdown", onHandleDown)
356
- .on("pointerup", onHandleUp)
357
- .on("pointerupoutside", onHandleUp);
358
- }
359
- createHandle({ cursor, name, shape = "square", }) {
360
- const handle = new Graphics();
361
- handle.label = name;
362
- handle.interactive = true;
363
- handle.alpha = this.handleOpacity;
364
- this.addToolTip(handle, name, cursor);
365
- if (shape === "circle") {
366
- handle.ellipse(this.controlsSize / 2, this.controlsSize / 2, this.controlsSize / 2, this.controlsSize / 2);
367
- }
368
- else
369
- handle.rect(0, 0, this.controlsSize, this.controlsSize);
370
- handle
371
- .stroke({ width: this.controlsStrokeThickness, color: this.lineColor })
372
- .fill(this.handleColor);
373
- handle.pivot.set(this.controlsSize / 2, this.controlsSize / 2);
374
- this.handleHandleEvents(handle, this);
375
- return handle;
376
- }
377
- createHandleIndicatorTo(handle, x, y) {
378
- x = x - this.position.x;
379
- y = y - this.position.y;
380
- const p1 = new Point(x, y);
381
- const origin = new Point(0, 0);
382
- const p = rotatePoint(p1, origin, -this.rotation);
383
- x = p.x;
384
- y = p.y;
385
- const size = this.controlsSize * 1.25;
386
- handle
387
- .clear()
388
- .moveTo(0, 0)
389
- .lineTo(x, y)
390
- .stroke({ width: 5, color: 0xffffff })
391
- .rect(x - size / 2, y - size / 2, size, size)
392
- .fill(0xffffff);
393
- this.deScale(handle);
394
- }
395
- deScale(target) {
396
- target.scale.set(1 / this.scale.x, 1 / this.scale.y);
397
- }
398
- // public methods:
399
- select(target) {
400
- if (!target) {
401
- this.unselect();
402
- return;
403
- }
404
- // copy object translation/transformation
405
- this.target = target;
406
- const bounds = target.getLocalBounds();
407
- this.width = bounds.width;
408
- this.height = bounds.height;
409
- this.scale.x = target.scale.x;
410
- this.scale.y = target.scale.y;
411
- this.x = target.x;
412
- this.y = target.y;
413
- this.rotation = target.rotation;
414
- let anchor;
415
- if (target.anchor) {
416
- anchor = target.anchor;
417
- }
418
- else if (target.pivot) {
419
- anchor = new Point(target.pivot.x / target.width, target.pivot.y / target.height);
420
- }
421
- else {
422
- anchor = new Point(0.5, 0.5);
423
- }
424
- target.anchor = anchor;
425
- this.left = -bounds.width * anchor.x;
426
- this.top = -bounds.height * anchor.y;
427
- this.bottom = bounds.height * (1 - anchor.y);
428
- this.right = bounds.width * (1 - anchor.x);
429
- // anchor mark
430
- this.generateAnchorMark?.(this.anchorMark);
431
- this.deScale(this.anchorMark);
432
- this.anchorMark.rotation = -this.rotation;
433
- const rotationHandlePos = {
434
- x: (this.left + this.right) / 2,
435
- y: this.top - 80 / Math.abs(this.scale.y),
436
- };
437
- // borders
438
- this.border
439
- .clear()
440
- .moveTo(this.left, this.top)
441
- .lineTo(this.right, this.top)
442
- .moveTo(this.left, this.bottom)
443
- .lineTo(this.right, this.bottom)
444
- .stroke({
445
- width: this.controlsStrokeThickness / this.scale.y,
446
- color: this.lineColor,
447
- })
448
- .moveTo(this.left, this.top)
449
- .lineTo(this.left, this.bottom)
450
- .moveTo(this.right, this.top)
451
- .lineTo(this.right, this.bottom)
452
- .stroke({
453
- width: this.controlsStrokeThickness / this.scale.x,
454
- color: this.lineColor,
455
- })
456
- .moveTo(rotationHandlePos.x, this.top)
457
- .lineTo(rotationHandlePos.x, rotationHandlePos.y)
458
- .stroke({
459
- width: this.controlsStrokeThickness / this.scale.x,
460
- color: this.lineColor,
461
- });
462
- // draw move hit area
463
- this.moveHandle.hitArea = new Rectangle(this.left, this.top, bounds.width, bounds.height);
464
- this.scaleTLHandle.position.set(this.left, this.top);
465
- this.scaleTRHandle.position.set(this.right, this.top);
466
- this.scaleBRHandle.position.set(this.right, this.bottom);
467
- this.scaleBLHandle.position.set(this.left, this.bottom);
468
- this.scaleTHandle.position.set(this.left + bounds.width / 2, this.top);
469
- this.scaleRHandle.position.set(this.right, this.top + bounds.height / 2);
470
- this.scaleBHandle.position.set(this.left + bounds.width / 2, this.bottom);
471
- this.scaleLHandle.position.set(this.left, this.top + bounds.height / 2);
472
- this.rotateHandle.position.set(rotationHandlePos.x, rotationHandlePos.y);
473
- const allHandles = [
474
- this.scaleTLHandle,
475
- this.scaleTRHandle,
476
- this.scaleBRHandle,
477
- this.scaleBLHandle,
478
- this.scaleTHandle,
479
- this.scaleRHandle,
480
- this.scaleBHandle,
481
- this.scaleLHandle,
482
- this.rotateHandle,
483
- ];
484
- // descale all handles
485
- allHandles.forEach((handle) => {
486
- this.deScale(handle);
487
- });
488
- this.visible = true;
489
- // console.log("select this - ", this);
490
- }
491
- unselect() {
492
- this.target = null;
493
- this.visible = false;
494
- }
495
- update() {
496
- if (this.target) {
497
- this.select(this.target);
498
- }
499
- }
500
- setTitle(title) {
501
- title = title || "";
502
- this.accessibleTitle = title;
503
- }
504
- setCursor(cursor) {
505
- const cursors = [
506
- "e-resize",
507
- "se-resize",
508
- "s-resize",
509
- "sw-resize",
510
- "w-resize",
511
- "nw-resize",
512
- "n-resize",
513
- "ne-resize",
514
- ];
515
- const index = cursors.indexOf(cursor);
516
- if (index >= 0) {
517
- const angle = 45;
518
- let rotation = this.target.angle;
519
- rotation = rotation + angle / 2;
520
- let newIndex = index + Math.floor(rotation / angle);
521
- newIndex = newIndex % cursors.length;
522
- document.body.style.cursor = cursors[newIndex];
523
- }
524
- else {
525
- document.body.style.cursor = cursor;
526
- }
527
- }
528
- generateAnchorMark(graphic) {
529
- const anchorInnerRadius = this.controlsSize * 0.125;
530
- const anchorOuterRadius = this.controlsSize * 0.625;
531
- graphic
532
- .clear()
533
- .circle(0, 0, this.controlsSize * 0.375)
534
- .moveTo(0, anchorInnerRadius)
535
- .lineTo(0, anchorOuterRadius)
536
- .moveTo(-anchorInnerRadius, 0)
537
- .lineTo(-anchorOuterRadius, 0)
538
- .moveTo(0, -anchorInnerRadius)
539
- .lineTo(0, -anchorOuterRadius)
540
- .moveTo(anchorInnerRadius, 0)
541
- .lineTo(anchorOuterRadius, 0)
542
- .fill(0xff0000)
543
- .stroke(0x111111);
544
- }
545
- get _target() {
546
- return this.target;
547
- }
548
- cleanup() { }
549
- }
package/dist/vector.d.ts DELETED
@@ -1,23 +0,0 @@
1
- import { Point } from "pixi.js";
2
- /**
3
- *
4
- * @param origin source from where distance will be calculated
5
- * @param p1 point 1 - positive direction is towards this point
6
- * @param p2 point 2
7
- * @returns 1 for same direction, -1 if the angle is more than 90 degrees
8
- */
9
- export declare function getDirection(origin: Point, p1: Point, p2: Point): -1 | 1;
10
- /**
11
- *
12
- * @param point point to rotate
13
- * @param origin origin point
14
- * @param rotation angle in radians
15
- * @returns rotated point
16
- */
17
- export declare function rotatePoint(point: Point, origin: Point, rotation: number): Point;
18
- /**
19
- * Calculate the euclidean distance between two points.
20
- */
21
- export declare function calcDistance(p1: Point, p2: Point): number;
22
- export declare function calcAngleDegrees(x: number, y: number): number;
23
- export declare function calcAngleRadians(x: number, y: number): number;
package/dist/vector.js DELETED
@@ -1,45 +0,0 @@
1
- import { Point } from "pixi.js";
2
- /**
3
- *
4
- * @param origin source from where distance will be calculated
5
- * @param p1 point 1 - positive direction is towards this point
6
- * @param p2 point 2
7
- * @returns 1 for same direction, -1 if the angle is more than 90 degrees
8
- */
9
- export function getDirection(origin, p1, p2) {
10
- const v1 = new Point(p1.x - origin.x, p1.y - origin.y);
11
- const v2 = new Point(p2.x - origin.x, p2.y - origin.y);
12
- const dot = v1.x * v2.x + v1.y * v2.y;
13
- return dot >= 0 ? 1 : -1;
14
- }
15
- /**
16
- *
17
- * @param point point to rotate
18
- * @param origin origin point
19
- * @param rotation angle in radians
20
- * @returns rotated point
21
- */
22
- export function rotatePoint(point, origin, rotation) {
23
- // Translate point to origin
24
- const translatedX = point.x - origin.x;
25
- const translatedY = point.y - origin.y;
26
- // Apply rotation
27
- const rotatedX = translatedX * Math.cos(rotation) - translatedY * Math.sin(rotation);
28
- const rotatedY = translatedX * Math.sin(rotation) + translatedY * Math.cos(rotation);
29
- // Translate point back
30
- const finalX = rotatedX + origin.x;
31
- const finalY = rotatedY + origin.y;
32
- return new Point(finalX, finalY);
33
- }
34
- /**
35
- * Calculate the euclidean distance between two points.
36
- */
37
- export function calcDistance(p1, p2) {
38
- return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
39
- }
40
- export function calcAngleDegrees(x, y) {
41
- return (calcAngleRadians(x, y) * 180) / Math.PI;
42
- }
43
- export function calcAngleRadians(x, y) {
44
- return Math.atan2(y, x);
45
- }