canvas-react-easy 1.0.9 → 1.1.0
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/dist/components/Border.js +17 -4
- package/dist/components/Tools.js +3 -2
- package/dist/tools/Text.js +5 -2
- package/dist/tools/TextHoverTool.js +43 -0
- package/dist/types/components/Border.d.ts +5 -3
- package/dist/types/components/Tools.d.ts +5 -1
- package/dist/types/tools/Text.d.ts +2 -0
- package/dist/types/tools/TextHoverTool.d.ts +13 -0
- package/package.json +1 -1
- package/src/components/Border.tsx +22 -5
- package/src/components/Tools.tsx +7 -5
- package/src/tools/Text.ts +20 -17
- package/src/tools/TextHoverTool.ts +58 -0
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect } from "react";
|
|
3
3
|
import Tools from "./Tools";
|
|
4
4
|
import Canvas from "./Canvas";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
5
|
+
import toolState from "../store/ToolState";
|
|
6
|
+
import CanvasState from "../store/CanvasState";
|
|
7
|
+
import { TextHoverTool } from "../tools/TextHoverTool";
|
|
8
|
+
const Border = ({ userName = "Гость" }) => {
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
var _a;
|
|
11
|
+
// активируем инструмент "имя пользователя" сразу после монтирования
|
|
12
|
+
if (CanvasState.canvas) {
|
|
13
|
+
(_a = toolState.tool) === null || _a === void 0 ? void 0 : _a.clearEvents();
|
|
14
|
+
const tool = new TextHoverTool(CanvasState.canvas, userName);
|
|
15
|
+
tool.listen();
|
|
16
|
+
toolState.setTool(tool);
|
|
17
|
+
}
|
|
18
|
+
}, [userName]);
|
|
19
|
+
return (_jsxs("div", { style: { position: "relative" }, children: [_jsx(Tools, { userName: userName }), _jsx(Canvas, {})] }));
|
|
20
|
+
};
|
|
8
21
|
export default Border;
|
package/dist/components/Tools.js
CHANGED
|
@@ -6,7 +6,8 @@ import { Rectangle } from "../tools/Rectangle";
|
|
|
6
6
|
import { Circle } from "../tools/Circle";
|
|
7
7
|
import { TextTool } from "../tools/Text";
|
|
8
8
|
import { ImageTool } from "../tools/ImageTool";
|
|
9
|
-
const Tools = () => {
|
|
9
|
+
const Tools = ({ userName = "Гость" }) => {
|
|
10
|
+
// универсальный выбор инструмента
|
|
10
11
|
const selectTool = (ToolClass) => {
|
|
11
12
|
var _a, _b;
|
|
12
13
|
if (!CanvasState.canvas)
|
|
@@ -18,6 +19,6 @@ const Tools = () => {
|
|
|
18
19
|
(_b = tool.listen) === null || _b === void 0 ? void 0 : _b.call(tool); // если инструмент имеет метод listen
|
|
19
20
|
toolState.setTool(tool);
|
|
20
21
|
};
|
|
21
|
-
return (_jsxs("div", { style: { marginBottom: "10px" }, children: [_jsx("button", { onClick: () => selectTool(Brush), children: "\u041A\u0438\u0441\u0442\u044C" }), _jsx("button", { onClick: () => selectTool(Rectangle), children: "\u041F\u0440\u044F\u043C\u043E\u0443\u0433\u043E\u043B\u044C\u043D\u0438\u043A" }), _jsx("button", { onClick: () => selectTool(Circle), children: "\u041A\u0440\u0443\u0433" }), _jsx("button", { onClick: () => selectTool(TextTool), children: "\u0422\u0435\u043A\u0441\u0442" }), _jsx("button", { onClick: () => selectTool(ImageTool), children: "\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435" })] }));
|
|
22
|
+
return (_jsxs("div", { style: { marginBottom: "10px", display: "flex", gap: "5px" }, children: [_jsx("button", { onClick: () => selectTool(Brush), children: "\u041A\u0438\u0441\u0442\u044C" }), _jsx("button", { onClick: () => selectTool(Rectangle), children: "\u041F\u0440\u044F\u043C\u043E\u0443\u0433\u043E\u043B\u044C\u043D\u0438\u043A" }), _jsx("button", { onClick: () => selectTool(Circle), children: "\u041A\u0440\u0443\u0433" }), _jsx("button", { onClick: () => selectTool(TextTool), children: "\u0422\u0435\u043A\u0441\u0442" }), _jsx("button", { onClick: () => selectTool(ImageTool), children: "\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435" })] }));
|
|
22
23
|
};
|
|
23
24
|
export default Tools;
|
package/dist/tools/Text.js
CHANGED
|
@@ -2,19 +2,22 @@ import Tool from "./Tool";
|
|
|
2
2
|
export class TextTool extends Tool {
|
|
3
3
|
constructor(canvas) {
|
|
4
4
|
super(canvas);
|
|
5
|
+
this.fontSize = 24; // размер шрифта в пикселях
|
|
6
|
+
this.fontFamily = "Arial"; // шрифт по умолчанию
|
|
5
7
|
this.listen();
|
|
6
8
|
}
|
|
7
9
|
listen() {
|
|
8
10
|
const handler = (e) => {
|
|
9
|
-
const evt = e;
|
|
11
|
+
const evt = e;
|
|
10
12
|
const canvas = evt.target;
|
|
11
13
|
const x = evt.pageX - canvas.offsetLeft;
|
|
12
14
|
const y = evt.pageY - canvas.offsetTop;
|
|
13
15
|
const text = prompt("Введите текст");
|
|
14
16
|
if (text) {
|
|
17
|
+
// Устанавливаем шрифт перед рисованием
|
|
18
|
+
this.ctx.font = `${this.fontSize}px ${this.fontFamily}`;
|
|
15
19
|
this.ctx.fillText(text, x, y);
|
|
16
20
|
}
|
|
17
|
-
// удаляем обработчик после первого использования
|
|
18
21
|
this.clearEvents();
|
|
19
22
|
};
|
|
20
23
|
this.addListener("mousedown", handler);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Tool from "./Tool";
|
|
2
|
+
export class TextHoverTool extends Tool {
|
|
3
|
+
constructor(canvas, userName) {
|
|
4
|
+
super(canvas);
|
|
5
|
+
this.fontSize = 18;
|
|
6
|
+
this.fontFamily = "Arial";
|
|
7
|
+
this.color = "black";
|
|
8
|
+
this.userName = userName;
|
|
9
|
+
// Создаём overlay-канвас поверх основного
|
|
10
|
+
this.overlayCanvas = document.createElement("canvas");
|
|
11
|
+
this.overlayCanvas.width = canvas.width;
|
|
12
|
+
this.overlayCanvas.height = canvas.height;
|
|
13
|
+
this.overlayCanvas.style.position = "absolute";
|
|
14
|
+
this.overlayCanvas.style.left = canvas.offsetLeft + "px";
|
|
15
|
+
this.overlayCanvas.style.top = canvas.offsetTop + "px";
|
|
16
|
+
this.overlayCanvas.style.pointerEvents = "none"; // мышь проходит через него
|
|
17
|
+
canvas.parentElement.appendChild(this.overlayCanvas);
|
|
18
|
+
this.overlayCtx = this.overlayCanvas.getContext("2d");
|
|
19
|
+
}
|
|
20
|
+
listen() {
|
|
21
|
+
const moveHandler = (evt) => {
|
|
22
|
+
const e = evt; // кастим к MouseEvent
|
|
23
|
+
// Очищаем overlay
|
|
24
|
+
this.overlayCtx.clearRect(0, 0, this.overlayCanvas.width, this.overlayCanvas.height);
|
|
25
|
+
// Координаты мыши относительно канваса
|
|
26
|
+
const x = e.pageX - this.canvas.offsetLeft;
|
|
27
|
+
const y = e.pageY - this.canvas.offsetTop;
|
|
28
|
+
// Рисуем имя чуть выше курсора
|
|
29
|
+
this.overlayCtx.font = `${this.fontSize}px ${this.fontFamily}`;
|
|
30
|
+
this.overlayCtx.fillStyle = this.color;
|
|
31
|
+
this.overlayCtx.fillText(this.userName, x, y - 20);
|
|
32
|
+
};
|
|
33
|
+
this.addListener("mousemove", moveHandler);
|
|
34
|
+
}
|
|
35
|
+
clearEvents() {
|
|
36
|
+
super.clearEvents();
|
|
37
|
+
// Удаляем overlay с DOM
|
|
38
|
+
if (this.overlayCanvas.parentElement) {
|
|
39
|
+
this.overlayCanvas.parentElement.removeChild(this.overlayCanvas);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export default TextHoverTool;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Tool from "./Tool";
|
|
2
|
+
export declare class TextHoverTool extends Tool {
|
|
3
|
+
userName: string;
|
|
4
|
+
fontSize: number;
|
|
5
|
+
fontFamily: string;
|
|
6
|
+
color: string;
|
|
7
|
+
private overlayCanvas;
|
|
8
|
+
private overlayCtx;
|
|
9
|
+
constructor(canvas: HTMLCanvasElement, userName: string);
|
|
10
|
+
listen(): void;
|
|
11
|
+
clearEvents(): void;
|
|
12
|
+
}
|
|
13
|
+
export default TextHoverTool;
|
package/package.json
CHANGED
|
@@ -1,14 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
2
|
import Tools from "./Tools";
|
|
3
3
|
import Canvas from "./Canvas";
|
|
4
|
+
import toolState from "../store/ToolState";
|
|
5
|
+
import CanvasState from "../store/CanvasState";
|
|
6
|
+
import { TextHoverTool } from "../tools/TextHoverTool";
|
|
7
|
+
|
|
8
|
+
interface BorderProps {
|
|
9
|
+
userName?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const Border: React.FC<BorderProps> = ({ userName = "Гость" }) => {
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// активируем инструмент "имя пользователя" сразу после монтирования
|
|
15
|
+
if (CanvasState.canvas) {
|
|
16
|
+
toolState.tool?.clearEvents();
|
|
17
|
+
const tool = new TextHoverTool(CanvasState.canvas, userName);
|
|
18
|
+
tool.listen();
|
|
19
|
+
toolState.setTool(tool);
|
|
20
|
+
}
|
|
21
|
+
}, [userName]);
|
|
4
22
|
|
|
5
|
-
const Border = observer(() => {
|
|
6
23
|
return (
|
|
7
|
-
<div>
|
|
8
|
-
<Tools />
|
|
24
|
+
<div style={{ position: "relative" }}>
|
|
25
|
+
<Tools userName={userName} />
|
|
9
26
|
<Canvas />
|
|
10
27
|
</div>
|
|
11
28
|
);
|
|
12
|
-
}
|
|
29
|
+
};
|
|
13
30
|
|
|
14
31
|
export default Border;
|
package/src/components/Tools.tsx
CHANGED
|
@@ -7,8 +7,12 @@ import { Circle } from "../tools/Circle";
|
|
|
7
7
|
import { TextTool } from "../tools/Text";
|
|
8
8
|
import { ImageTool } from "../tools/ImageTool";
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface ToolsProps {
|
|
11
|
+
userName?: string;
|
|
12
|
+
}
|
|
11
13
|
|
|
14
|
+
const Tools: React.FC<ToolsProps> = ({ userName = "Гость" }) => {
|
|
15
|
+
// универсальный выбор инструмента
|
|
12
16
|
const selectTool = (ToolClass: any) => {
|
|
13
17
|
if (!CanvasState.canvas) return;
|
|
14
18
|
|
|
@@ -19,17 +23,15 @@ const Tools = () => {
|
|
|
19
23
|
const tool = new ToolClass(CanvasState.canvas);
|
|
20
24
|
tool.listen?.(); // если инструмент имеет метод listen
|
|
21
25
|
toolState.setTool(tool);
|
|
22
|
-
};
|
|
26
|
+
};
|
|
23
27
|
|
|
24
|
-
|
|
25
28
|
return (
|
|
26
|
-
<div style={{ marginBottom: "10px" }}>
|
|
29
|
+
<div style={{ marginBottom: "10px", display: "flex", gap: "5px" }}>
|
|
27
30
|
<button onClick={() => selectTool(Brush)}>Кисть</button>
|
|
28
31
|
<button onClick={() => selectTool(Rectangle)}>Прямоугольник</button>
|
|
29
32
|
<button onClick={() => selectTool(Circle)}>Круг</button>
|
|
30
33
|
<button onClick={() => selectTool(TextTool)}>Текст</button>
|
|
31
34
|
<button onClick={() => selectTool(ImageTool)}>Изображение</button>
|
|
32
|
-
|
|
33
35
|
</div>
|
|
34
36
|
);
|
|
35
37
|
};
|
package/src/tools/Text.ts
CHANGED
|
@@ -5,25 +5,28 @@ export class TextTool extends Tool {
|
|
|
5
5
|
super(canvas);
|
|
6
6
|
this.listen();
|
|
7
7
|
}
|
|
8
|
+
fontSize: number = 24; // размер шрифта в пикселях
|
|
9
|
+
fontFamily: string = "Arial"; // шрифт по умолчанию
|
|
8
10
|
|
|
9
11
|
listen() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
const handler = (e: Event) => {
|
|
13
|
+
const evt = e as MouseEvent;
|
|
14
|
+
const canvas = evt.target as HTMLCanvasElement;
|
|
15
|
+
const x = evt.pageX - canvas.offsetLeft;
|
|
16
|
+
const y = evt.pageY - canvas.offsetTop;
|
|
17
|
+
|
|
18
|
+
const text = prompt("Введите текст");
|
|
19
|
+
if (text) {
|
|
20
|
+
// Устанавливаем шрифт перед рисованием
|
|
21
|
+
this.ctx.font = `${this.fontSize}px ${this.fontFamily}`;
|
|
22
|
+
this.ctx.fillText(text, x, y);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
this.clearEvents();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
this.addListener("mousedown", handler);
|
|
29
|
+
}
|
|
27
30
|
|
|
28
31
|
onClickHandler(e: MouseEvent) {
|
|
29
32
|
const target = e.target as HTMLCanvasElement;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Tool from "./Tool";
|
|
2
|
+
|
|
3
|
+
export class TextHoverTool extends Tool {
|
|
4
|
+
userName: string;
|
|
5
|
+
fontSize: number = 18;
|
|
6
|
+
fontFamily: string = "Arial";
|
|
7
|
+
color: string = "black";
|
|
8
|
+
|
|
9
|
+
private overlayCanvas!: HTMLCanvasElement;
|
|
10
|
+
private overlayCtx!: CanvasRenderingContext2D;
|
|
11
|
+
|
|
12
|
+
constructor(canvas: HTMLCanvasElement, userName: string) {
|
|
13
|
+
super(canvas);
|
|
14
|
+
this.userName = userName;
|
|
15
|
+
|
|
16
|
+
// Создаём overlay-канвас поверх основного
|
|
17
|
+
this.overlayCanvas = document.createElement("canvas");
|
|
18
|
+
this.overlayCanvas.width = canvas.width;
|
|
19
|
+
this.overlayCanvas.height = canvas.height;
|
|
20
|
+
this.overlayCanvas.style.position = "absolute";
|
|
21
|
+
this.overlayCanvas.style.left = canvas.offsetLeft + "px";
|
|
22
|
+
this.overlayCanvas.style.top = canvas.offsetTop + "px";
|
|
23
|
+
this.overlayCanvas.style.pointerEvents = "none"; // мышь проходит через него
|
|
24
|
+
canvas.parentElement!.appendChild(this.overlayCanvas);
|
|
25
|
+
|
|
26
|
+
this.overlayCtx = this.overlayCanvas.getContext("2d")!;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
listen() {
|
|
30
|
+
const moveHandler = (evt: Event) => {
|
|
31
|
+
const e = evt as MouseEvent; // кастим к MouseEvent
|
|
32
|
+
|
|
33
|
+
// Очищаем overlay
|
|
34
|
+
this.overlayCtx.clearRect(0, 0, this.overlayCanvas.width, this.overlayCanvas.height);
|
|
35
|
+
|
|
36
|
+
// Координаты мыши относительно канваса
|
|
37
|
+
const x = e.pageX - this.canvas.offsetLeft;
|
|
38
|
+
const y = e.pageY - this.canvas.offsetTop;
|
|
39
|
+
|
|
40
|
+
// Рисуем имя чуть выше курсора
|
|
41
|
+
this.overlayCtx.font = `${this.fontSize}px ${this.fontFamily}`;
|
|
42
|
+
this.overlayCtx.fillStyle = this.color;
|
|
43
|
+
this.overlayCtx.fillText(this.userName, x, y - 20);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
this.addListener("mousemove", moveHandler);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
clearEvents() {
|
|
50
|
+
super.clearEvents();
|
|
51
|
+
// Удаляем overlay с DOM
|
|
52
|
+
if (this.overlayCanvas.parentElement) {
|
|
53
|
+
this.overlayCanvas.parentElement.removeChild(this.overlayCanvas);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default TextHoverTool;
|