canvas-react-easy 1.0.7 → 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.
@@ -1,8 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { observer } from "mobx-react-lite";
2
+ import { useEffect } from "react";
3
3
  import Tools from "./Tools";
4
4
  import Canvas from "./Canvas";
5
- const Border = observer(() => {
6
- return (_jsxs("div", { children: [_jsx(Tools, {}), _jsx(Canvas, {})] }));
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;
@@ -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;
@@ -15,21 +15,19 @@ export class ImageTool extends Tool {
15
15
  }
16
16
  listen() {
17
17
  const handler = (e) => {
18
- const canvas = e.target;
19
- const x = e.pageX - canvas.offsetLeft;
20
- const y = e.pageY - canvas.offsetTop;
18
+ const evt = e;
19
+ const canvas = evt.target;
20
+ const x = evt.pageX - canvas.offsetLeft;
21
+ const y = evt.pageY - canvas.offsetTop;
21
22
  const url = prompt("Введите ссылку на изображение");
22
23
  if (!url)
23
24
  return;
24
25
  const img = new Image();
25
26
  img.src = url;
26
- img.onload = () => {
27
- this.ctx.drawImage(img, x, y);
28
- };
29
- // удаляем обработчик после первого использования
30
- canvas.removeEventListener("mousedown", handler);
27
+ img.onload = () => this.ctx.drawImage(img, x, y);
28
+ this.clearEvents(); // удаляем обработчики после использования
31
29
  };
32
- this.canvas.addEventListener("mousedown", handler);
30
+ this.addListener("mousedown", handler);
33
31
  }
34
32
  onClickHandler(e) {
35
33
  return __awaiter(this, void 0, void 0, function* () {
@@ -2,21 +2,25 @@ 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 target = e.target;
10
- const x = e.pageX - target.offsetLeft;
11
- const y = e.pageY - target.offsetTop;
11
+ const evt = e;
12
+ const canvas = evt.target;
13
+ const x = evt.pageX - canvas.offsetLeft;
14
+ const y = evt.pageY - canvas.offsetTop;
12
15
  const text = prompt("Введите текст");
13
16
  if (text) {
17
+ // Устанавливаем шрифт перед рисованием
18
+ this.ctx.font = `${this.fontSize}px ${this.fontFamily}`;
14
19
  this.ctx.fillText(text, x, y);
15
20
  }
16
- // убираем событие после первого использования
17
- target.removeEventListener("mousedown", handler);
21
+ this.clearEvents();
18
22
  };
19
- this.canvas.addEventListener("mousedown", handler);
23
+ this.addListener("mousedown", handler);
20
24
  }
21
25
  onClickHandler(e) {
22
26
  const target = e.target;
@@ -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;
@@ -1,10 +1,23 @@
1
1
  export default class Tool {
2
2
  constructor(canvas) {
3
3
  this.canvas = canvas;
4
+ this.events = [];
4
5
  this.ctx = canvas.getContext("2d");
6
+ this.events = [];
5
7
  this.clearEvents();
6
8
  }
9
+ // сохраняем слушатели, чтобы потом удалять
10
+ addListener(type, handler) {
11
+ this.canvas.addEventListener(type, handler);
12
+ this.events.push({ type, handler });
13
+ }
7
14
  clearEvents() {
15
+ // удаляем все зарегистрированные слушатели
16
+ this.events.forEach(({ type, handler }) => {
17
+ this.canvas.removeEventListener(type, handler);
18
+ });
19
+ this.events = [];
20
+ // очищаем стандартные свойства
8
21
  this.canvas.onmousedown = null;
9
22
  this.canvas.onmouseup = null;
10
23
  this.canvas.onmousemove = null;
@@ -1,4 +1,6 @@
1
- declare const Border: (() => import("react/jsx-runtime").JSX.Element) & {
2
- displayName: string;
3
- };
1
+ import React from "react";
2
+ interface BorderProps {
3
+ userName?: string;
4
+ }
5
+ declare const Border: React.FC<BorderProps>;
4
6
  export default Border;
@@ -1,2 +1,6 @@
1
- declare const Tools: () => import("react/jsx-runtime").JSX.Element;
1
+ import React from "react";
2
+ interface ToolsProps {
3
+ userName?: string;
4
+ }
5
+ declare const Tools: React.FC<ToolsProps>;
2
6
  export default Tools;
@@ -1,6 +1,8 @@
1
1
  import Tool from "./Tool";
2
2
  export declare class TextTool extends Tool {
3
3
  constructor(canvas: HTMLCanvasElement);
4
+ fontSize: number;
5
+ fontFamily: string;
4
6
  listen(): void;
5
7
  onClickHandler(e: MouseEvent): void;
6
8
  }
@@ -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;
@@ -2,5 +2,10 @@ export default class Tool {
2
2
  canvas: HTMLCanvasElement;
3
3
  constructor(canvas: HTMLCanvasElement);
4
4
  ctx: CanvasRenderingContext2D;
5
+ events: Array<{
6
+ type: string;
7
+ handler: EventListener;
8
+ }>;
9
+ addListener(type: string, handler: EventListener): void;
5
10
  clearEvents(): void;
6
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvas-react-easy",
3
- "version": "1.0.7",
3
+ "version": "1.1.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/types/index.d.ts",
6
6
  "license": "MIT",
@@ -1,14 +1,31 @@
1
- import { observer } from "mobx-react-lite";
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;
@@ -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
- const Tools = () => {
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
  };
@@ -7,25 +7,23 @@ export class ImageTool extends Tool {
7
7
  }
8
8
 
9
9
  listen() {
10
- const handler = (e: MouseEvent) => {
11
- const canvas = e.target as HTMLCanvasElement;
12
- const x = e.pageX - canvas.offsetLeft;
13
- const y = e.pageY - canvas.offsetTop;
10
+ const handler = (e: Event) => {
11
+ const evt = e as MouseEvent;
12
+ const canvas = evt.target as HTMLCanvasElement;
13
+ const x = evt.pageX - canvas.offsetLeft;
14
+ const y = evt.pageY - canvas.offsetTop;
14
15
 
15
16
  const url = prompt("Введите ссылку на изображение");
16
17
  if (!url) return;
17
18
 
18
19
  const img = new Image();
19
20
  img.src = url;
20
- img.onload = () => {
21
- this.ctx.drawImage(img, x, y);
22
- };
21
+ img.onload = () => this.ctx.drawImage(img, x, y);
23
22
 
24
- // удаляем обработчик после первого использования
25
- canvas.removeEventListener("mousedown", handler);
23
+ this.clearEvents(); // удаляем обработчики после использования
26
24
  };
27
25
 
28
- this.canvas.addEventListener("mousedown", handler);
26
+ this.addListener("mousedown", handler);
29
27
  }
30
28
 
31
29
  async onClickHandler(e: MouseEvent) {
package/src/tools/Text.ts CHANGED
@@ -5,20 +5,27 @@ 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
- const handler = (e: MouseEvent) => {
11
- const target = e.target as HTMLCanvasElement;
12
- const x = e.pageX - target.offsetLeft;
13
- const y = e.pageY - target.offsetTop;
14
- const text = prompt("Введите текст");
15
- if (text) {
16
- this.ctx.fillText(text, x, y);
17
- }
18
- // убираем событие после первого использования
19
- target.removeEventListener("mousedown", handler);
20
- };
21
- this.canvas.addEventListener("mousedown", handler);
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);
22
29
  }
23
30
 
24
31
  onClickHandler(e: MouseEvent) {
@@ -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;
package/src/tools/Tool.ts CHANGED
@@ -1,12 +1,27 @@
1
1
  export default class Tool {
2
2
  constructor(public canvas: HTMLCanvasElement) {
3
3
  this.ctx = canvas.getContext("2d")!;
4
+ this.events = [];
4
5
  this.clearEvents();
5
6
  }
6
7
 
7
8
  ctx: CanvasRenderingContext2D;
9
+ events: Array<{ type: string; handler: EventListener }> = [];
10
+
11
+ // сохраняем слушатели, чтобы потом удалять
12
+ addListener(type: string, handler: EventListener) {
13
+ this.canvas.addEventListener(type, handler);
14
+ this.events.push({ type, handler });
15
+ }
8
16
 
9
17
  clearEvents() {
18
+ // удаляем все зарегистрированные слушатели
19
+ this.events.forEach(({ type, handler }) => {
20
+ this.canvas.removeEventListener(type, handler);
21
+ });
22
+ this.events = [];
23
+
24
+ // очищаем стандартные свойства
10
25
  this.canvas.onmousedown = null;
11
26
  this.canvas.onmouseup = null;
12
27
  this.canvas.onmousemove = null;