cbvirtua 1.0.48 → 1.0.49

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.
Files changed (72) hide show
  1. package/canvas-example-main/canvas-example-main/.github/workflows/main.yml +62 -0
  2. package/canvas-example-main/canvas-example-main/README.md +13 -0
  3. package/canvas-example-main/canvas-example-main/curved.html +52 -0
  4. package/canvas-example-main/canvas-example-main/eslint.config.js +30 -0
  5. package/canvas-example-main/canvas-example-main/index.html +13 -0
  6. package/canvas-example-main/canvas-example-main/package.json +51 -0
  7. package/canvas-example-main/canvas-example-main/pnpm-lock.yaml +4760 -0
  8. package/canvas-example-main/canvas-example-main/postcss.config.js +6 -0
  9. package/canvas-example-main/canvas-example-main/public/vite.svg +1 -0
  10. package/canvas-example-main/canvas-example-main/src/App.tsx +17 -0
  11. package/canvas-example-main/canvas-example-main/src/assets/github.svg +1 -0
  12. package/canvas-example-main/canvas-example-main/src/assets/react.svg +1 -0
  13. package/canvas-example-main/canvas-example-main/src/components/Iconfont/demo.css +539 -0
  14. package/canvas-example-main/canvas-example-main/src/components/Iconfont/demo_index.html +418 -0
  15. package/canvas-example-main/canvas-example-main/src/components/Iconfont/iconfont.css +55 -0
  16. package/canvas-example-main/canvas-example-main/src/components/Iconfont/iconfont.js +1 -0
  17. package/canvas-example-main/canvas-example-main/src/components/Iconfont/iconfont.json +79 -0
  18. package/canvas-example-main/canvas-example-main/src/components/Iconfont/iconfont.ttf +0 -0
  19. package/canvas-example-main/canvas-example-main/src/components/Iconfont/iconfont.woff +0 -0
  20. package/canvas-example-main/canvas-example-main/src/components/Iconfont/iconfont.woff2 +0 -0
  21. package/canvas-example-main/canvas-example-main/src/components/Iconfont/index.tsx +39 -0
  22. package/canvas-example-main/canvas-example-main/src/main.css +9 -0
  23. package/canvas-example-main/canvas-example-main/src/main.tsx +10 -0
  24. package/canvas-example-main/canvas-example-main/src/pages/2048/g2048.ts +14 -0
  25. package/canvas-example-main/canvas-example-main/src/pages/2048/index.tsx +21 -0
  26. package/canvas-example-main/canvas-example-main/src/pages/clock/index.tsx +103 -0
  27. package/canvas-example-main/canvas-example-main/src/pages/demo/index.tsx +21 -0
  28. package/canvas-example-main/canvas-example-main/src/pages/editor/components/editor/index.module.less +3 -0
  29. package/canvas-example-main/canvas-example-main/src/pages/editor/components/editor/index.tsx +99 -0
  30. package/canvas-example-main/canvas-example-main/src/pages/editor/components/header/index.module.less +5 -0
  31. package/canvas-example-main/canvas-example-main/src/pages/editor/components/header/index.tsx +5 -0
  32. package/canvas-example-main/canvas-example-main/src/pages/editor/components/material/index.module.less +59 -0
  33. package/canvas-example-main/canvas-example-main/src/pages/editor/components/material/index.tsx +85 -0
  34. package/canvas-example-main/canvas-example-main/src/pages/editor/components/setting/index.module.less +7 -0
  35. package/canvas-example-main/canvas-example-main/src/pages/editor/components/setting/index.tsx +5 -0
  36. package/canvas-example-main/canvas-example-main/src/pages/editor/core/application.ts +35 -0
  37. package/canvas-example-main/canvas-example-main/src/pages/editor/core/cmp/base.ts +17 -0
  38. package/canvas-example-main/canvas-example-main/src/pages/editor/core/cmp/factory.ts +14 -0
  39. package/canvas-example-main/canvas-example-main/src/pages/editor/core/cmp/shape.tsx +43 -0
  40. package/canvas-example-main/canvas-example-main/src/pages/editor/core/editor.ts +61 -0
  41. package/canvas-example-main/canvas-example-main/src/pages/editor/core/type.ts +6 -0
  42. package/canvas-example-main/canvas-example-main/src/pages/editor/index.module.less +7 -0
  43. package/canvas-example-main/canvas-example-main/src/pages/editor/index.tsx +32 -0
  44. package/canvas-example-main/canvas-example-main/src/pages/editor/store/component-config.ts +61 -0
  45. package/canvas-example-main/canvas-example-main/src/pages/editor/store/components.ts +43 -0
  46. package/canvas-example-main/canvas-example-main/src/pages/editor/store/layout.ts +40 -0
  47. package/canvas-example-main/canvas-example-main/src/pages/home/index.tsx +59 -0
  48. package/canvas-example-main/canvas-example-main/src/pages/jigsaw/index.tsx +3 -0
  49. package/canvas-example-main/canvas-example-main/src/pages/minesweeper/bomber.png +0 -0
  50. package/canvas-example-main/canvas-example-main/src/pages/minesweeper/index.tsx +138 -0
  51. package/canvas-example-main/canvas-example-main/src/pages/minesweeper/mark.png +0 -0
  52. package/canvas-example-main/canvas-example-main/src/pages/minesweeper/minesweeper.ts +345 -0
  53. package/canvas-example-main/canvas-example-main/src/pages/minesweeper/utils.ts +24 -0
  54. package/canvas-example-main/canvas-example-main/src/pages/pageflip/index.tsx +200 -0
  55. package/canvas-example-main/canvas-example-main/src/pages/pageflip/page1.jpg +0 -0
  56. package/canvas-example-main/canvas-example-main/src/pages/practice/draw/index.ts +367 -0
  57. package/canvas-example-main/canvas-example-main/src/pages/practice/index.module.less +26 -0
  58. package/canvas-example-main/canvas-example-main/src/pages/practice/index.tsx +54 -0
  59. package/canvas-example-main/canvas-example-main/src/pages/shape-editor/control.ts +174 -0
  60. package/canvas-example-main/canvas-example-main/src/pages/shape-editor/editor.ts +91 -0
  61. package/canvas-example-main/canvas-example-main/src/pages/shape-editor/index.raw.tsx +159 -0
  62. package/canvas-example-main/canvas-example-main/src/pages/shape-editor/index.tsx +36 -0
  63. package/canvas-example-main/canvas-example-main/src/pages/shape-editor/shape.ts +248 -0
  64. package/canvas-example-main/canvas-example-main/src/router.tsx +53 -0
  65. package/canvas-example-main/canvas-example-main/src/utils/storage.ts +48 -0
  66. package/canvas-example-main/canvas-example-main/src/vite-env.d.ts +1 -0
  67. package/canvas-example-main/canvas-example-main/tailwind.config.js +8 -0
  68. package/canvas-example-main/canvas-example-main/tsconfig.app.json +30 -0
  69. package/canvas-example-main/canvas-example-main/tsconfig.json +7 -0
  70. package/canvas-example-main/canvas-example-main/tsconfig.node.json +22 -0
  71. package/canvas-example-main/canvas-example-main/vite.config.ts +18 -0
  72. package/package.json +1 -1
@@ -0,0 +1,174 @@
1
+ import { App, Ellipse, Line, UI } from 'leafer-ui';
2
+ import Editor from './editor';
3
+
4
+ export interface IControl {
5
+ offsetX: number;
6
+ offsetY: number;
7
+ effect?: string;
8
+ }
9
+
10
+ const circleRadius = 12;
11
+
12
+ export interface IControlConfig {
13
+ points?: UI[];
14
+ controls?: IControl[][];
15
+ onMove?: (e) => void;
16
+ onMoveEnd?: (e) => void;
17
+ }
18
+
19
+ export default class Control {
20
+ config: IControlConfig;
21
+ app: App;
22
+ editor: Editor;
23
+ private controlEls: UI[] = [];
24
+
25
+ point: UI;
26
+ control: IControl[];
27
+
28
+ private pointsIdxMap: Map<number, number> = new Map();
29
+
30
+ private controlIdxMap: Map<number, number> = new Map();
31
+
32
+ constructor(app: App, config: IControlConfig) {
33
+ this.config = config;
34
+ this.app = app;
35
+
36
+ this.config.points.forEach((point, index) => {
37
+ this.pointsIdxMap.set(point.innerId, index);
38
+ });
39
+
40
+ const { onMove = () => {}, onMoveEnd = () => {} } = this.config;
41
+
42
+ this.editor = new Editor(this.app, {
43
+ onMove: (e) => {
44
+ const controlIdx = this.controlIdxMap.get(e.target.innerId);
45
+ const pointIdx = this.pointsIdxMap.get(e.target.innerId);
46
+
47
+ if (controlIdx !== undefined) {
48
+ const offsetX = e.x - this.point.x;
49
+ const offsetY = e.y - this.point.y;
50
+
51
+ this.control[controlIdx].offsetX = offsetX;
52
+ this.control[controlIdx].offsetY = offsetY;
53
+ this.draw();
54
+ onMove({ offsetX, offsetY });
55
+ }
56
+
57
+ if (pointIdx !== undefined) {
58
+ this.point = this.config.points[pointIdx];
59
+ this.control = this.config.controls[pointIdx];
60
+ this.draw();
61
+ }
62
+ },
63
+
64
+ onTap: (e) => {
65
+ const idx = this.pointsIdxMap.get(e.target.innerId);
66
+
67
+ if (idx !== undefined) {
68
+ if (this.point && this.point.innerId !== e.target.innerId) {
69
+ this.reset();
70
+ }
71
+
72
+ this.point = this.config.points[idx];
73
+ this.control = this.config.controls[idx];
74
+ this.draw();
75
+ } else {
76
+ this.reset();
77
+ }
78
+ },
79
+ onMoveEnd: (e) => {
80
+ onMoveEnd(e);
81
+ },
82
+ });
83
+ this.draw();
84
+ }
85
+
86
+ private draw() {
87
+ if (!this.point || !this.control?.length) return;
88
+
89
+ this.clear();
90
+
91
+ const [leftControl, rightControl] = this.control || [];
92
+ if (leftControl) {
93
+ const x = leftControl.offsetX + this.point.x;
94
+ const y = leftControl.offsetY + this.point.y;
95
+
96
+ const leftPoint = new Ellipse({
97
+ x,
98
+ y,
99
+ width: circleRadius,
100
+ height: circleRadius,
101
+ fill: 'rgb(50,205,121)',
102
+ cursor: 'move',
103
+ });
104
+
105
+ this.controlIdxMap.set(leftPoint.innerId, 0);
106
+
107
+ this.controlEls.push(leftPoint);
108
+ this.app.tree.add(leftPoint);
109
+
110
+ const leftLine = new Line({
111
+ points: [
112
+ x + circleRadius / 2,
113
+ y + circleRadius / 2,
114
+ this.point.x + this.point.width / 2,
115
+ this.point.y + this.point.height / 2,
116
+ ],
117
+ width: 1,
118
+ stroke: 'rgb(50,205,121)',
119
+ });
120
+
121
+ this.controlEls.push(leftLine);
122
+
123
+ this.app.tree.add(leftLine);
124
+ }
125
+ if (rightControl) {
126
+ const x = rightControl.offsetX + this.point.x;
127
+ const y = rightControl.offsetY + this.point.y;
128
+
129
+ const rightPoint = new Ellipse({
130
+ x: rightControl.offsetX + this.point.x,
131
+ y: rightControl.offsetY + this.point.y,
132
+ width: circleRadius,
133
+ height: circleRadius,
134
+ fill: 'rgb(50,205,121)',
135
+ cursor: 'move',
136
+ });
137
+ this.controlEls.push(rightPoint);
138
+
139
+ this.controlIdxMap.set(rightPoint.innerId, 1);
140
+ this.app.tree.add(rightPoint);
141
+
142
+ const rightLine = new Line({
143
+ points: [
144
+ this.point.x + this.point.width / 2,
145
+ this.point.y + this.point.height / 2,
146
+ x + circleRadius / 2,
147
+ y + circleRadius / 2,
148
+ ],
149
+ width: 1,
150
+ stroke: 'rgb(50,205,121)',
151
+ });
152
+
153
+ this.controlEls.push(rightLine);
154
+
155
+ this.app.tree.add(rightLine);
156
+ }
157
+ }
158
+
159
+ update(config: IControlConfig) {
160
+ this.config = { ...this.config, ...config };
161
+ }
162
+
163
+ clear() {
164
+ this.controlEls.forEach((el) => {
165
+ this.app.tree.remove(el);
166
+ });
167
+ }
168
+
169
+ reset() {
170
+ this.clear();
171
+ this.point = null;
172
+ this.control = [];
173
+ }
174
+ }
@@ -0,0 +1,91 @@
1
+ import { App, PointerEvent, UI } from 'leafer-ui';
2
+
3
+ export interface IEditorConfig {
4
+ onMove?: (e) => void;
5
+ onMoveEnd?: (e) => void;
6
+ onTap?: (e) => void;
7
+ onDown?: (e) => void;
8
+ }
9
+
10
+ interface ISelector {
11
+ _offsetX: number;
12
+ _offsetY: number;
13
+ target: UI;
14
+ }
15
+
16
+ export default class Editor {
17
+ app: App;
18
+ config: IEditorConfig;
19
+ isMouseDown: boolean;
20
+ isMoving: boolean;
21
+ selector: ISelector;
22
+
23
+ constructor(app: App, config: IEditorConfig) {
24
+ this.app = app;
25
+ this.config = config;
26
+
27
+ const {
28
+ onMove = () => {},
29
+ onMoveEnd = () => {},
30
+ onTap = () => {},
31
+ onDown = () => {},
32
+ } = this.config;
33
+
34
+ this.app.on(PointerEvent.MOVE, (e) => {
35
+ const { x, y } = e;
36
+
37
+ if (this.isMouseDown) {
38
+ if (this.selector && this.selector.target.cursor === 'move') {
39
+ this.isMoving = true;
40
+
41
+ const { _offsetX, _offsetY } = this.selector;
42
+
43
+ this.selector.target.x = x - _offsetX;
44
+ this.selector.target.y = y - _offsetY;
45
+
46
+ onMove({
47
+ target: this.selector.target,
48
+ x,
49
+ y,
50
+ offsetX: x - this.selector.target.x,
51
+ offsetY: y - this.selector.target.y,
52
+ });
53
+ }
54
+ }
55
+ });
56
+
57
+ this.app.on(PointerEvent.DOWN, (e) => {
58
+ this.isMouseDown = true;
59
+
60
+ if (this.app !== e.target) {
61
+ const { x, y } = e.target;
62
+
63
+ const _offsetX = e.x - x;
64
+ const _offsetY = e.y - y;
65
+
66
+ this.selector = { target: e.target, _offsetX, _offsetY };
67
+ onDown({ target: e.target });
68
+ }
69
+ });
70
+
71
+ this.app.on(PointerEvent.UP, (e) => {
72
+ if (this.isMoving) {
73
+ onMoveEnd({ target: e.target });
74
+ }
75
+ this.isMouseDown = false;
76
+ this.isMoving = false;
77
+ });
78
+
79
+ this.app.on(PointerEvent.TAP, (e) => {
80
+ const { x, y } = e;
81
+
82
+ if (this.app === e.target) {
83
+ this.selector = null;
84
+ onTap({ target: e.target, x, y });
85
+ } else {
86
+ this.selector.target = e.target;
87
+ onTap({ target: e.target, x, y });
88
+ }
89
+ });
90
+ }
91
+ }
@@ -0,0 +1,159 @@
1
+ // import { useEffect, useRef } from 'react';
2
+
3
+ // const canvasWidth = 800;
4
+ // const canvasHeight = 800;
5
+
6
+ // export default function ShapeEditor() {
7
+ // const canvasRef = useRef<HTMLCanvasElement>();
8
+
9
+ // useEffect(() => {
10
+ // const canvas = canvasRef.current;
11
+ // const ctx = canvasRef.current.getContext('2d');
12
+
13
+ // const points = [
14
+ // [300, 300],
15
+ // [240, 400],
16
+ // [300, 500],
17
+ // [400, 500],
18
+ // [460, 400],
19
+ // [400, 400],
20
+ // ];
21
+
22
+ // const radius = 8;
23
+
24
+ // const drawArc = (x, y) => {
25
+ // ctx.beginPath();
26
+ // ctx.arc(x, y, radius, 0, Math.PI * 2);
27
+ // ctx.strokeStyle = 'lime';
28
+ // ctx.lineWidth = 2;
29
+ // ctx.stroke();
30
+ // };
31
+
32
+ // const drawPol = () => {
33
+ // ctx.clearRect(0, 0, canvasWidth, canvasHeight);
34
+ // ctx.beginPath();
35
+
36
+ // ctx.moveTo(points[0][0], points[0][1]);
37
+
38
+ // const P1 = points[0];
39
+ // const P2 = points[1];
40
+ // const P3 = points[2];
41
+
42
+ // const AC = { x: P3[0] - P1[0], y: P3[1] - P1[1] };
43
+
44
+ // const ACLen = Math.sqrt(AC.x ** 2 + AC.y ** 2);
45
+
46
+ // const u = { x: AC.x / ACLen, y: AC.y / ACLen };
47
+
48
+ // const P21 = { x: P2[0] - 100 * u.x, y: P2[1] - 100 * u.y };
49
+ // const P22 = { x: P2[0] + 50 * u.x, y: P2[1] + 50 * u.y };
50
+
51
+ // drawArc(P21.x, P21.y);
52
+ // drawArc(P22.x, P22.y);
53
+
54
+ // ctx.moveTo(P1[0], P1[1]);
55
+ // ctx.quadraticCurveTo(P21.x, P21.y, P2[0], P2[1]);
56
+ // ctx.quadraticCurveTo(P22.x, P22.y, P3[0], P3[1]);
57
+
58
+ // points.slice(3).forEach((point) => {
59
+ // ctx.lineTo(point[0], point[1]);
60
+ // });
61
+ // ctx.strokeStyle = 'red';
62
+ // ctx.lineWidth = 1;
63
+ // ctx.closePath();
64
+ // ctx.stroke();
65
+
66
+ // points.forEach((point) => {
67
+ // drawArc(point[0], point[1]);
68
+ // });
69
+ // };
70
+
71
+ // let isEnter = false;
72
+ // let isDown = false;
73
+
74
+ // let moveX = null,
75
+ // moveY = null;
76
+
77
+ // let movePoint = null;
78
+
79
+ // const handleMousemove = (e) => {
80
+ // const rect = canvas.getBoundingClientRect();
81
+ // const x = e.clientX - rect.left;
82
+ // const y = e.clientY - rect.top;
83
+
84
+ // if (isDown) {
85
+ // canvas.style.cursor = 'pointer';
86
+ // if (!movePoint || moveX === null || moveY === null) return;
87
+ // movePoint[0] = movePoint[0] - moveX + x;
88
+ // movePoint[1] = movePoint[1] - moveY + y;
89
+ // moveX = movePoint[0];
90
+ // moveY = movePoint[1];
91
+ // drawPol();
92
+ // } else if (isEnter) {
93
+ // canvas.style.cursor = 'pointer';
94
+ // } else {
95
+ // for (const point of points) {
96
+ // const dx = x - point[0];
97
+ // const dy = y - point[1];
98
+ // const _isEnter = Math.sqrt(dx * dx + dy * dy) <= radius;
99
+
100
+ // if (_isEnter) {
101
+ // isEnter = _isEnter;
102
+ // canvas.style.cursor = 'pointer';
103
+ // return;
104
+ // }
105
+ // }
106
+ // }
107
+ // canvas.style.cursor = 'default';
108
+ // };
109
+
110
+ // const handleMouseDown = (e) => {
111
+ // if (!isEnter) return;
112
+ // isDown = true;
113
+ // const rect = canvas.getBoundingClientRect();
114
+ // const x = e.clientX - rect.left;
115
+ // const y = e.clientY - rect.top;
116
+
117
+ // for (const point of points) {
118
+ // const dx = x - point[0];
119
+ // const dy = y - point[1];
120
+ // const _isEnter = Math.sqrt(dx * dx + dy * dy) <= radius;
121
+
122
+ // if (_isEnter) {
123
+ // moveX = x;
124
+ // moveY = y;
125
+ // movePoint = point;
126
+ // break;
127
+ // }
128
+ // }
129
+ // };
130
+ // const handleMouseUp = (e) => {
131
+ // isDown = false;
132
+ // moveX = null;
133
+ // moveY = null;
134
+ // movePoint = null;
135
+ // };
136
+
137
+ // canvas.addEventListener('mousemove', handleMousemove);
138
+ // canvas.addEventListener('mousedown', handleMouseDown);
139
+ // canvas.addEventListener('mouseup', handleMouseUp);
140
+
141
+ // drawPol();
142
+
143
+ // return () => {
144
+ // canvas.removeEventListener('mousemove', handleMousemove);
145
+ // canvas.removeEventListener('mousedown', handleMouseDown);
146
+ // canvas.removeEventListener('mouseup', handleMouseUp);
147
+ // };
148
+ // }, []);
149
+
150
+ // return (
151
+ // <div className="w-[100vw] h-[100vh] flex items-center justify-center background-[#aaa] flex-col">
152
+ // <canvas
153
+ // width={canvasWidth}
154
+ // height={canvasHeight}
155
+ // ref={canvasRef}
156
+ // ></canvas>
157
+ // </div>
158
+ // );
159
+ // }
@@ -0,0 +1,36 @@
1
+ import { useEffect } from 'react';
2
+ import { useWindowSize } from 'react-use';
3
+ import ShapeEditor from './shape';
4
+
5
+ export default function Component() {
6
+ const { width, height } = useWindowSize();
7
+ const centerX = width / 2,
8
+ centerY = height / 2;
9
+
10
+ const initPoints = [
11
+ { x: centerX - 40, y: centerY - 80 },
12
+ { x: centerX + 80, y: centerY },
13
+ { x: centerX + 80, y: centerY + 80 },
14
+ { x: centerX - 40, y: centerY + 160 },
15
+ { x: centerX + 200 - 160, y: centerY + 200 + 160 },
16
+ { x: centerX + 200 - 200, y: centerY + 200 + 80 },
17
+ { x: centerX + 200 - 200, y: centerY + 200 },
18
+ { x: 500, y: 500 },
19
+ ];
20
+
21
+ useEffect(() => {
22
+ const shapeEditor = new ShapeEditor({
23
+ points: initPoints.reduce((acc, point) => {
24
+ acc.push(point.x, point.y);
25
+ return acc;
26
+ }, []),
27
+ });
28
+ return () => {
29
+ shapeEditor.destroy();
30
+ };
31
+ }, []);
32
+
33
+ return (
34
+ <div className="w-[100vw] h-[100vh] flex items-center justify-center background-[#aaa] flex-col"></div>
35
+ );
36
+ }
@@ -0,0 +1,248 @@
1
+ import { App, Ellipse, Group, IGroupInputData, Pen, UI } from 'leafer-ui';
2
+ import Editor from './editor';
3
+ import Control, { IControl } from './control';
4
+
5
+ const circleRadius = 14;
6
+
7
+ interface IPoint {
8
+ x: number;
9
+ y: number;
10
+ controls: IControl[];
11
+ }
12
+
13
+ export interface IConfig extends IGroupInputData {
14
+ points: IPoint[];
15
+ onMoveEnd?: (e) => void;
16
+ }
17
+
18
+ export default class ShapeEditor {
19
+ app: App;
20
+ editor: Editor;
21
+
22
+ selector: Ellipse;
23
+ private points: Ellipse[] = [];
24
+
25
+ private controls: [IControl, IControl][] = [
26
+ [
27
+ { offsetX: -50, offsetY: 0 },
28
+ { offsetX: 50, offsetY: 0 },
29
+ ],
30
+ [
31
+ {
32
+ offsetX: -50,
33
+ offsetY: -50,
34
+ },
35
+ {
36
+ offsetX: 50,
37
+ offsetY: 50,
38
+ },
39
+ ],
40
+ ];
41
+
42
+ private pointsIdxMap: Map<number, number> = new Map();
43
+
44
+ config: IConfig = { points: [] };
45
+ private pen: Pen = new Pen({ fill: 'black' });
46
+
47
+ private selectBox: Ellipse = null;
48
+
49
+ private control: Control;
50
+
51
+ private container: Group;
52
+
53
+ constructor(config: IConfig) {
54
+ const { points, x, y } = config;
55
+ this.config = config;
56
+ this.app = new App({ view: window, tree: {} });
57
+
58
+ this.container = new Group({ x, y });
59
+
60
+ this.app.tree.add(this.container);
61
+ this.container.add(this.pen);
62
+
63
+ const { onMoveEnd = () => {} } = this.config;
64
+
65
+ this.editor = new Editor(this.app, {
66
+ onMove: (e) => {
67
+ const idx = this.pointsIdxMap.get(e.target.innerId);
68
+ if (idx !== undefined) {
69
+ this.selector = e.target;
70
+ this.drawSelectBox();
71
+ this.drawPolygon();
72
+ }
73
+ },
74
+ onTap: (e) => {
75
+ if (this.pointsIdxMap.get(e.target.innerId) !== undefined) {
76
+ this.selector = e.target;
77
+ this.drawSelectBox();
78
+ return;
79
+ }
80
+
81
+ if (e.target === this.pen.pathElement) {
82
+ const { x, y } = e;
83
+
84
+ this.addPoint(x - circleRadius / 2, y - circleRadius / 2);
85
+ }
86
+
87
+ this.selector = null;
88
+ this.clearSelectBox();
89
+ },
90
+ onMoveEnd(e) {
91
+ onMoveEnd(e);
92
+ },
93
+ });
94
+
95
+ this.drawPoints(points);
96
+
97
+ this.control = new Control(this.app, {
98
+ points: this.points,
99
+ controls: this.controls,
100
+ onMove: () => {
101
+ this.drawPolygon();
102
+ },
103
+ onMoveEnd: (e) => {
104
+ console.log(e);
105
+ },
106
+ });
107
+
108
+ this.drawPolygon();
109
+ }
110
+
111
+ private addPoint(x, y) {
112
+ const arc = new Ellipse({
113
+ x,
114
+ y,
115
+ width: circleRadius,
116
+ height: circleRadius,
117
+ fill: 'rgb(50,205,121)',
118
+ cursor: 'move',
119
+ });
120
+
121
+ this.points.push(arc);
122
+
123
+ this.pointsIdxMap.set(arc.innerId, this.points.length - 1);
124
+
125
+ this.control?.update({ points: this.points });
126
+
127
+ this.addEls(arc);
128
+ }
129
+
130
+ private addEls(nodes: UI | UI[]) {
131
+ if (!Array.isArray(nodes)) {
132
+ nodes = [nodes];
133
+ }
134
+
135
+ this.container.addMany(...nodes);
136
+ }
137
+
138
+ private removeEls(nodes: UI | UI[]) {
139
+ if (!Array.isArray(nodes)) {
140
+ nodes = [nodes];
141
+ }
142
+
143
+ nodes.forEach((node) => {
144
+ this.container.remove(node);
145
+ });
146
+ }
147
+
148
+ private drawPoints(points) {
149
+ this.removeEls(this.points);
150
+
151
+ for (let i = 1; i < points.length; i += 2) {
152
+ const x = points[i - 1],
153
+ y = points[i];
154
+ this.addPoint(x, y);
155
+ }
156
+
157
+ this.drawPolygon();
158
+ }
159
+
160
+ private drawPolygon() {
161
+ this.pen.clear();
162
+
163
+ const points = this.points.map(({ x, y }) => {
164
+ return { x: x + circleRadius / 2, y: y + circleRadius / 2 };
165
+ });
166
+
167
+ this.pen.setStyle({ stroke: 'lime', fill: '#FF4B4B' });
168
+
169
+ this.pen.moveTo(points[0].x, points[0].y);
170
+ points.forEach((point, index) => {
171
+ const controls = this.controls[index];
172
+
173
+ let nextIdx = index + 1;
174
+ if (nextIdx >= points.length) nextIdx = 0;
175
+
176
+ const leftPoint = point,
177
+ rightPoint = points[nextIdx];
178
+
179
+ let controlLeft = null,
180
+ controlRight = null;
181
+
182
+ if (controls) {
183
+ controlLeft = controls[1];
184
+ }
185
+ const nextControls = this.controls[nextIdx];
186
+
187
+ if (nextControls) {
188
+ controlRight = nextControls[0];
189
+ }
190
+
191
+ if (!controlLeft && !controlRight) {
192
+ rightPoint.x > 500
193
+ ? this.pen.moveTo(rightPoint.x, rightPoint.y)
194
+ : this.pen.lineTo(rightPoint.x, rightPoint.y);
195
+ } else if (controlLeft && !controlRight) {
196
+ this.pen.quadraticCurveTo(
197
+ controlLeft.offsetX + leftPoint.x,
198
+ controlLeft.offsetY + leftPoint.y,
199
+ rightPoint.x,
200
+ rightPoint.y
201
+ );
202
+ } else if (!controlLeft && controlRight) {
203
+ this.pen.quadraticCurveTo(
204
+ controlRight.offsetX + rightPoint.x,
205
+ controlRight.offsetY + rightPoint.y,
206
+ rightPoint.x,
207
+ rightPoint.y
208
+ );
209
+ } else {
210
+ this.pen.bezierCurveTo(
211
+ controlLeft.offsetX + leftPoint.x,
212
+ controlLeft.offsetY + leftPoint.y,
213
+ controlRight.offsetX + rightPoint.x,
214
+ controlRight.offsetY + rightPoint.y,
215
+ rightPoint.x,
216
+ rightPoint.y
217
+ );
218
+ }
219
+ });
220
+ }
221
+
222
+ private drawSelectBox() {
223
+ if (!this.selector) return;
224
+
225
+ if (this.selectBox) this.removeEls(this.selectBox);
226
+
227
+ const { x, y, width, height } = this.selector;
228
+
229
+ this.selectBox = new Ellipse({
230
+ x: x - 2,
231
+ y: y - 2,
232
+ width: width + 4,
233
+ height: height + 4,
234
+ stroke: '#FF4B4B',
235
+ strokeWidth: 1,
236
+ });
237
+
238
+ this.addEls(this.selectBox);
239
+ }
240
+
241
+ private clearSelectBox() {
242
+ if (this.selectBox) this.removeEls(this.selectBox);
243
+ }
244
+
245
+ destroy() {
246
+ this.app.destroy();
247
+ }
248
+ }