Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add draw image tool (#200) #201

Merged
merged 1 commit into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ English | [中文](./README_zh.md)

## Features

1. Creation and editing of graphics, including: rounded rectangles, ellipses, lines, paths (polyline), text, polygons, stars;
1. Creation and editing of graphics, including: rounded rectangle, ellipse, line, path, text, polygon, star;
2. Path editing using the pen tool, adjusting control points;
3. Support tools: selection tool, shape drawing tools, pen tool, canvas tool, hand tool;
3. Support tools: selection tool, graphics drawing tools, image tool, pen tool, canvas tool, hand tool;
4. Infinite canvas, with zoom and pan capabilities;
5. History, with undo and redo;
6. i18n;
7. Snap support, currently supports pixel grid snap and shape guide snap;
7. Snap support, currently supports pixel grid snap and graphics guide snap;
8. Keyboard shortcuts;
9. Copy and paste, supports cross-canvas copy and paste, alignment, and arrangement;
10. Layers panel, properties panel;
11. Group, Frame;
12. Multi-user collaborative editing (requires backend implementation);
13. Ruler;
14. Import and export JSON;
14. Import and export JSON data;
15. User settings;

Next Steps
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

1. 图形的创建和编辑,包括:圆角矩形、椭圆、线、路径(多段线)、文本、多边形、星形;
2. 使用钢笔工具进行路径编辑,调整控制点;
3. 丰富的工具:选中工具、绘制图形工具、钢笔工具、画布工具、抓手工具;
3. 丰富的工具:选中工具、绘制图形工具、绘制图形工具、钢笔工具、画布工具、抓手工具;
4. 无限画布,可以缩放和拖拽画布;
5. 历史记录,可撤销重做;
6. 国际化;
Expand Down
90 changes: 90 additions & 0 deletions packages/core/src/tools/tool_draw_img.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { type IRect, normalizeRect } from '@suika/geo';

import { type SuikaEditor } from '../editor';
import { SuikaRect } from '../graphics';
import { PaintType } from '../paint';
import { DrawGraphicsTool } from './tool_draw_graphics';
import { type ITool } from './type';

interface ImgData {
url: string;
name: string;
}

const uploadImg = () => {
return new Promise<ImgData>((resolve) => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'image/*'; // only image

fileInput.onchange = (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
const src = e.target?.result as string;
setTimeout(() => {
resolve({ url: src, name: file.name });
});
};
reader.readAsDataURL(file);
};

fileInput.click();
});
};

const TYPE = 'drawImg';
const HOTKEY = '';

export class DrawImgTool extends DrawGraphicsTool implements ITool {
static override readonly type = TYPE;
static override readonly hotkey = HOTKEY;
override readonly type = TYPE;
override readonly hotkey = HOTKEY;

private imgData: ImgData | null = null;

constructor(editor: SuikaEditor) {
super(editor);
this.commandDesc = 'Add Image';
}

async enableActive() {
try {
const imgData = await uploadImg();
await this.editor.imgManager.addImg(imgData.url);
this.imgData = imgData;
return true;
} catch (error) {
return false;
}
}

protected override createGraphics(rect: IRect) {
rect = normalizeRect(rect);
const graphics = new SuikaRect(
{
objectName: this.imgData!.name,
width: rect.width,
height: rect.height,
fill: [
{
type: PaintType.Image,
attrs: {
src: this.imgData!.url,
},
},
],
},
{
advancedAttrs: {
x: rect.x,
y: rect.y,
},
doc: this.editor.doc,
},
);
return graphics;
}
}
17 changes: 14 additions & 3 deletions packages/core/src/tools/tool_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { type IKey } from '../key_binding_manager';
import { DragCanvasTool } from './tool_drag_canvas';
import { DrawEllipseTool } from './tool_draw_ellipse';
import { DrawFrameTool } from './tool_draw_frame';
import { DrawImgTool } from './tool_draw_img';
import { DrawLineTool } from './tool_draw_line';
import { DrawPathTool } from './tool_draw_path';
import { DrawRectTool } from './tool_draw_rect';
Expand Down Expand Up @@ -46,6 +47,7 @@ export class ToolManager {
this.registerToolCtor(DrawFrameTool);
this.registerToolCtor(DrawRectTool);
this.registerToolCtor(DrawEllipseTool);
this.registerToolCtor(DrawImgTool);
this.registerToolCtor(DrawLineTool);
this.registerToolCtor(DrawTextTool);
this.registerToolCtor(DragCanvasTool);
Expand All @@ -60,6 +62,7 @@ export class ToolManager {
DrawFrameTool.type,
DrawRectTool.type,
DrawEllipseTool.type,
DrawImgTool.type,
DrawPathTool.type,
PencilTool.type,
DrawLineTool.type,
Expand Down Expand Up @@ -265,7 +268,7 @@ export class ToolManager {
this._unbindEvent = noop;
this.unbindHotkey();
}
setActiveTool(toolName: string) {
async setActiveTool(toolName: string) {
if (!this.enableSwitchTool || this.getActiveToolName() === toolName) {
return;
}
Expand All @@ -275,12 +278,20 @@ export class ToolManager {
return;
}

const prevTool = this.currentTool;
const currentToolCtor = this.toolCtorMap.get(toolName) || null;
if (!currentToolCtor) {
throw new Error(`tool "${toolName}" is not registered`);
}
const currentTool = (this.currentTool = new currentToolCtor(this.editor));
const currentTool = new currentToolCtor(this.editor);

if (currentTool.enableActive) {
const canActive = await currentTool.enableActive();
if (!canActive) {
return;
}
}
const prevTool = this.currentTool;
this.currentTool = currentTool;

prevTool && prevTool.onInactive();
this.setCursorWhenActive();
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/tools/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export interface ITool extends IBaseTool {
hotkey: string | IKey;
type: string;
cursor: ICursor;

enableActive?: () => Promise<boolean>;
onMoveExcludeDrag: (event: PointerEvent, isOutsideCanvas: boolean) => void;
getDragBlockStep?: () => number;
}
Expand Down
19 changes: 19 additions & 0 deletions packages/icons/src/icons/tool/image-outlined.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';

export const ImageOutlined = React.memo(() => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M18 5H5V13.6849L8.46807 9.78333L17.1983 18H18V5ZM5 18V15.1901L8.53193 11.2167L15.7392 18H5ZM4 4V19H19V4H4ZM16 9.5C16 10.3284 15.3284 11 14.5 11C13.6716 11 13 10.3284 13 9.5C13 8.67157 13.6716 8 14.5 8C15.3284 8 16 8.67157 16 9.5ZM17 9.5C17 10.8807 15.8807 12 14.5 12C13.1193 12 12 10.8807 12 9.5C12 8.11929 13.1193 7 14.5 7C15.8807 7 17 8.11929 17 9.5Z"
/>
</svg>
);
});
1 change: 1 addition & 0 deletions packages/icons/src/icons/tool/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './ellipse-outlined';
export * from './frame-outline';
export * from './hand-outlined';
export * from './image-outlined';
export * from './line-outlined';
export * from './menu-outlined';
export * from './polygon-outlined';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
EllipseOutlined,
FrameOutlined,
HandOutlined,
ImageOutlined,
LineOutlined,
PencilOutlined,
PenOutlined,
Expand Down Expand Up @@ -86,6 +87,12 @@ export const ToolBar = () => {
intlId: 'tool.ellipse',
icon: <EllipseOutlined />,
},
drawImg: {
name: 'drawImg',
hotkey: '',
intlId: 'tool.image',
icon: <ImageOutlined />,
},
pathSelect: {
name: 'pathSelect',
hotkey: 'V',
Expand Down
1 change: 1 addition & 0 deletions packages/suika/src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"tool.frame": "Frame",
"tool.rectangle": "Rectangle",
"tool.ellipse": "Ellipse",
"tool.image": "Image",
"tool.pen": "Pen",
"tool.line": "Line",
"tool.polygon": "Polygon",
Expand Down
1 change: 1 addition & 0 deletions packages/suika/src/locale/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"tool.frame": "画板",
"tool.rectangle": "矩形",
"tool.ellipse": "圆",
"tool.image": "图片",
"tool.pen": "钢笔",
"tool.line": "直线",
"tool.polygon": "多边形",
Expand Down
Loading