- 
                Notifications
    
You must be signed in to change notification settings  - Fork 1.4k
 
Structure
๋ด๋ถ์ ์ผ๋ก ํฌ๊ฒ ๋ ๊ฐ์ง ๋ ์ด์ด๋ก ๋ถ๋ฆฌ๊ฐ ๋๋ฉฐ ์๋์ ๊ฐ๋ค.
- 
ImageEditor- API๋ฅผ ๋ด๋นํ๋ ๊ฐ์ฒด๋ก ์๋ ๋ ๊ฐ์ง ๋ ์ด์ด๋ฅผ ๊ฐ์ง๋ฉฐ, UI์ ์ง์  ์ปค๋ฎค๋์ผ์ด์  ํ๋ค.- 
Middle Layerย - ๊ทธ๋ฆฌ๊ธฐ ๋์๊ณผ ๋ฌด๊ดํ ์ดํ๋ฆฌ์ผ์ด์ ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ์งํฉ์ผ๋ก Invoker, Command, CommandFactory๋ก ๊ตฌ์ฑ๋๋ค. - 
Graphics Layerย - ์ผ๋ฐ์ ์ผ๋ก ๋งํ๋ canvas๋ฅผ ImageEditor์์ ์ ๊ณตํ๋ ๊ธฐ๋ฅ๋ค๋ก๋ง ์ด๋ฃจ์ด์ง ยCanvasย ๋ฅผ ๊ตฌ์ฑํ๋ค. ๊ทธ๋ฆฌ๊ธฐ ๋์์ ์ถ์ํํ๊ณ ์ค์  ๊ตฌํ์ย fabric.jsย ๋ฅผ ์ฌ์ฉํ๋ค.- 
Component๋ ํน์  ๊ธฐ๋ฅ์ ๊ทธ๋ฆฌ๊ธฐ ๋์์ ๋ชจ๋ํ ํ ๊ฒ์ผ๋ก Graphics Layer์ ์์๋๋ค. - ๋๋ก์ ๋ชจ๋ ๋ํ Graphics Layer์์ ํ์ํ ๊ธฐ๋ฅ์ผ๋ก ์ฌ๊ธฐ์์ ๋ด๋นํ๋ค.
 
 - 
 
 - 
 
API๋ฅผ ๋ด๋นํ๋ ๊ฐ์ฒด๋ก ์๋์ ์ ์ํ Invoker์ Graphics๋ฅผ ํ๋กํผํฐ๋ก ๊ฐ์ง๋ค.
Command๋ฅผ ๋ฑ๋ก, ์์ฑํ๋ ํด๋์ค์ด๋ค. Command๋ฅผ ๋ฑ๋กํ  ์ ์๋ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๊ณ  ImageEditor๋ Command๋ก ๋๋ก์ ๋์์ ํด์ผ ํ๋ ๊ณณ์์ ๋ฑ๋ก๋ Command ์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ค.
- register - Command ๋ฑ๋ก
 - create - ๋ฑ๋ก๋ Command ์ ์ธ์คํด์ค ์์ฑ
 
Command๋ ํน์  ๊ธฐ๋ฅ์ ์ํํ๊ธฐ ์ํธ ์คํ ๋จ์์ด๋ฉฐ ๋ค๋ฅธ ๋ชจ๋๊ณผ ๋
๋ฆฝ์ ์ด๋ค. ์ด๋ฏธ์ง ์๋ํฐ์์๋ Undo/Redo๋ฅผ ํ๊ธฐ ์ํ ์คํ ๋จ์๋ก ์ฌ์ฉ๋๋ฉฐ Command ์ธ์คํด์ค๋ฅผ Invoker์์ ์คํ์ผ๋ก ๊ด๋ฆฌํ๋ค.
Command ๋ฑ๋ก์ name, execute, undo๋ฅผ ์ ์ํ ๊ฐ์ฒด actions์ args๋ฅผ ์ ๋ฌ ๋ฐ๋๋ค.
class Command {
    constructor(actions, args) {
        this.name = actions.name;
        this.args = args;
        this.execute = actions.execute;
        this.undo = actions.undo;
        this.executeCallback = actions.executeCallback || null;
        this.undoCallback = actions.undoCallback || null;
        this.undoData = {};
    }
}import๊ฐ ๋ ๋ CommandFactory.register๋ฅผ ์ํํ๋ค. Command ์์๋ ์ฌ์ฉํ Component๋ฅผ import ํ๊ณ Graphics ์ธ์คํด์ค๋ฅผ ์ ๋ฌํ์ฌ ๊ธฐ๋ฅ์ํํ๊ฒ ํ๋ค.
// commandName.js
import commandFactory from '../factory/command';
const command = {
   name: 'commandName',
   execute(graphics, ...args) {
   },
   undo(graphics, ...args) {
   }
};
CommandFactory.register(command);
module.export = command;const command = commandFactory.create('commandName', param1, param2);
command.execute(...command.args).then(value => {
    // push undo stack
    return value;
}).catch(message => {
    // do something
    return Promise.reject(message);
})Command๋ฅผ ์คํํ๊ณ  Undo/Redo๋ฅผ ๊ด๋ฆฌํ๋ค. ๋ค๋ฅธ ๊ธฐ๋ฅ์ ์ ๊ฑฐํ๋ค.
- 
Component๋ฆฌ์คํธ ๊ด๋ฆฌ๋ ์ฌ๊ธฐ์๋ ์ ๊ฑฐํ๋ค. ์๋์ ๋ช ์ธํ Canvas๋ก ์ด๊ดํ๋ค. 
ImageEditor์ ๋๋ก์ ์ฝ์ด ๋ชจ๋๋ก ์ฐ๋ฆฌ๊ฐ ์ ๊ณตํ๋ ๋๋ก์ ๊ธฐ๋ฅ๋ค์ ๋ชจ๋ ๋ค์ด ์๋ค. ํ๋ถ์ ์ค์  ๊ทธ๋ํฝ ๋ชจ๋์ fabric.js ๋ฅผ ์ฌ์ฉํ๋ค. ์๋๋ ์ด๋ฏธ์ง ์๋ํฐ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ ๋ชฉ๋ก์ด๋ค.(๋ถ์ ์์ ์ ๊ท API)
| Icon | Image | Shape | Text | Flip | FreeDrawing | LineDrawing | Rotate | Crop | ๊ธฐํ | 
|---|---|---|---|---|---|---|---|---|---|
| addIcon | addImageObject | addShape | addText | flipX | setBrush | setBrush | rotate | crop | resizeCanvasDimension | 
| registerIcons | loadImageFromFile | setDrawingShape | changeText | flipY | setAngle | getCropzoneRect | toDataURL | ||
| changeIconColor | loadImageFromURL | changeShape | changeTextStyle | resetFlip | getDrawingMode | ||||
| ApplyFilter | setDrawingMode | ||||||||
| RemoveFilter | getImageName | ||||||||
| hasFilter | clearObjects | ||||||||
| removeActiveObject | |||||||||
| destroy | |||||||||
| setDefaultPathStyle | 
<Canvas ๊ด๋ จ ์ด๋ฒคํธ>
Canvas ๋ CustomEvents๋ฅผ ย mixin ํ๊ณ  Canvas ์ ๊ด๋ จํ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ํ์๊ฐ ์์ ๋ on("eventName", callback); ์ ์ฌ์ฉํ๋ค.
์๋๋ ๋ฆฌํฉํ ๋ง ์ด์  ๊ธฐ์ค์ผ๋ก fabric.js ๋ก๋ถํฐ ๋ฐ์ํ ์ด๋ฒคํธ ์ค์ ๋ณ๊ฒฝ๋  ์ด๋ฒคํธ๋ค์ด๋ค.
| ์ด๋ฒคํธ ์ด๋ฆ | ํธ๋ค๋ฌ | from | to | ์ธ๋ถ์ด๋ฒคํธ ์ด๋ฆ | ์ฉ๋ | 
|---|---|---|---|---|---|
| path:created | _onCreatePath | fabric | ImageEditor ->Canvas๋ก ์ด๋ ์์   | 
N/A | path ์คํ์ผ์ ํ๋๋ก ์ง์ ํ์ฌ ์ฌ์ฉ | 
Component๋ ๊ธฐ์กด๊ณผ ๋์ผํ๊ฒ ํน์  ๋๋ก์ ๋์์ ๊ตฌํํ ๋ชจ๋์ด๋ค.
๋ค๋ฅธ ์ ์ Component๋ ๋๋ก์ ์งํฉ์ ๋ถ๋ถ ์งํฉ์ด๋ฏ๋ก Canvas๋ฅผ ํตํ์ฌ์ ์ฌ์ฉํ๊ฒ ํ๋ค. ย ์ด์ ์๋ Command์์ Component๋ฅผ ์ง์  ์ฌ์ฉํ์์ง๋ง ๋ฐ๋ ๊ตฌ์กฐ์์๋ Command๋ Canvas๋ฅผ ์ฌ์ฉํ๋ค. Canvas๋ ๋ค์ํ Component๋ค์ ๊ด๋ฆฌํ๋ค.
๋ํ Component์์ ์ธ๋ถ๋ก ์ ๋ฌํด์ผ ํ๋ ์ด๋ฒคํธ๋ Canvas๋ฅผ ํตํ์ฌ ์ ๋ฌํ๋๋ก ํ๋ค. Canvas๋ ํด๋น ์ด๋ฒคํธ๋ฅผ Canvas ์ธ๋ถ์์ ๋ฑ๋กํ ๊ฒฝ์ฐ ๋ค์ ์ ๋ฌํ๋ค.
Component ๋ชฉ๋ก์ ์๋์ ๊ฐ๊ณ  ๊ธฐ์กด์ start/end์ ๊ฐ์ด ๋ชจ๋ ๋ณ๊ฒฝ์ด ํ์ํ Component๋ ํ์ํ์๋ค.
| ์ด๋ฆ | ๋ชจ๋ ํ์ | ์ฉ๋ | to-be | 
|---|---|---|---|
| Cropper | O | Crop ๋ชจ๋, Crop์ฉ UI์ ์ด๋ฒคํธ ์ฒ๋ฆฌ ํ์ | ์ ์ง | 
| filter | X | ์ด๋ฏธ์ง ํํฐ ๋ชจ๋ | ์ ์ง | 
| flip | X | ์ด๋ฏธ์ง ๋ค์ง๊ธฐ ๋ชจ๋ | ์ ์ง | 
| freeDrawing | O | ์์ ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋ | ์ ์ง | 
| icon | X | ์์ด์ฝ ์ถ๊ฐ ๋ชจ๋ | ์ ์ง | 
| imageLoader | X | ๋ฉ์ธ ์ด๋ฏธ์ง ๋ก๋ฉ ๋ชจ๋ | ์ ์ง | 
| line | O | ์ง์ ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋ | ์ ์ง | 
| rotation | X | ๋ฉ์ธ ์ด๋ฏธ์ง ๋ฐ ์ค๋ธ์ ํธ๋ค ํ์  ๋ชจ๋ | ์ ์ง | 
| shape | O | ๋ํ ๊ทธ๋ฆฌ๊ธฐ ๋ชจ๋ | ์ ์ง | 
| text | O | ํ ์คํธ ์ค๋ธ์ ํธ ์ ๋ ฅ ๋ชจ๋ | ์ ์ง | 
| main | X | ๊ณตํต ํ๋กํผํฐ ๊ด๋ฆฌ | ์ ๊ฑฐ ๋ฐ ๊ธฐ๋ฅ Canvas๋ก ์ด๊ด | 
๋๋ก์ ๋ชจ๋๋ ํ ์๊ฐ์ ํ๋๋ง ํ์ฑํ ๋์ด์ผ ํ๋๋ฐ, ๊ฐ ๋ชจ๋๋ง๋ค ์ฌ์ฉํ๋ ์ด๋ฒคํธ์ UI๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฌ๋ฏ๋ก ๋๋ก์ ๋ชจ๋๋ ์ํธ ๋ฒ ํ์ ์ด๋ค.
๊ทธ์ ๋ฐํด Command๋ ๋๋ก์ ๋์์ ๋ํด์ ์ ์ํ ๋ช
๋ น์ด๊ธฐ ๋๋ฌธ์ ๋๋ก์ ๋ชจ๋์ ๋ฌด๊ดํ๊ฒ ๋์ ๊ฐ๋ฅํ๋ค๊ณ  ๋ณผ ์ ์๋ค. ๋๋ก์ ๋ชจ๋์ ์ข
์์ฑ์ ์์ ๊ณ  ๊ทธ ์ด์์ ์ฌ์ฉ์์๊ฒ ์์ํ๋ค. ๋๋ก์ ๋ชจ๋์ Command ์ ์คํ ์ฌ๋ถ๋ฅผ ๊ด๋ฆฌํ์ง ์์์ผ๋ก์จ ๊ตฌํ์ ๊ฐ๊ฒฐ์ฑ๊ณผ ๋ฒ๊ทธ ๋ฐ์ ๊ฐ๋ฅ์ฑ, ์ ์ง๋ณด์์ ๋ํ ์ด์ ์ ์ป์ ์ ์๋ค. ์ด๋ฏธ์ง ์๋ํฐ๋ ์ฌ์ฉ์๊ฐ ์๋์ ๊ฐ์ด ์์์ ์ธ API ํธ์ถ์ ํ๋ค๊ณ  ๋ฏฟ๋๋ค.
<์์์  ์ฌ์ฉ ์>
editor.setDrawingMode("cropper");
editor.crop(editor.getCropzoneRect());
editor.setDrawingMode("normal");
<๋ฌ๊ธ์๋ ์ฌ์ฉ์>
editor.setDrawingMode("cropper");
editor.rotate(90);
editor.setDrawingMode("normal");
๋ฌ๊ธ์๋ ์ฌ์ฉ์์ด์ง๋ง, ๋๋ก์ ๋ชจ๋์ Command๊ฐ ๋ฌด๊ดํจ์ ๋ณด์ฌ์ฃผ๋ ํ๋์ ์๊ฐ ๋ ์ ์๋ค.
๋ณ๊ฒฝ๋ API์ ๋๋ก์๋ชจ๋/Command์ ๋ฌด๊ด์ฑ ๊ฐ๋ ์ ๋ฐํ์ผ๋ก ๋ด๋ถ ์ค๊ณ์ ๋ฆฌํฉํ ๋ง์ ์งํํ๋ค.
Canvas Layer์ ๋ชจ๋ํ๋ฅผ ์ํ์ฌ Canvas ๋ด๋ถ์์ ์ผ์ด๋๋ ๋ชจ๋ ์ด๋ฒคํธ๋ Canvas๋ฅผ ํตํ์ฌ ์ธ๋ถ๋ก ์ ๋ฌํ๋ค. Canvas๊ฐ ๊ด๋ฆฌํ๋ Component์์ ์ผ์ด๋๋ ์ด๋ฒคํธ๋ Canvas์๊ฒ ์ ๋ฌํ๊ณ Canvas๊ฐ ์ธ๋ถ๋ก ์ ๋ฌํ๋ค. ImageEditor๋ ์ธ๋ถ์ ์ง์ ์ ์ผ๋ก ์ปค๋ฎค๋์ผ์ด์ ์ ํ๋ฏ๋ก UI๋ก ์ ๋ฌํ๋ ๋ชจ๋ ์ฝ๋ฐฑ์ ImageEditor ํตํ๋ค. ์๋ฅผ ๋ค์ด ย Canvas์ ย Component๋ก๋ถํฐ ์ฌ๋ผ์์ผ ํ๋ ์ด๋ฒคํธ๋ ImageEditor ๋ด์์ ย Canvas์ ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ์ฌ ์ ๋ฌ ๋ฐ๊ฒ ํ๋ค Canvas์์ ์ฌ๋ผ์จ ์ด๋ฒคํธ๊ฐ undo ์คํ์ผ๋ก ๊ด๋ฆฌ๋์ด์ผ ํ๋ ๊ฒฝ์ฐ ImageEditor ์์ Canvas๋ก ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ย Invoker ํจ์๋ฅผ ํธ์ถํ๋ค.
UI ๋ก ์ ๋ฌ๋๋ ์ด๋ฒคํธ๋ ๋๋ถ๋ถ Promise ๋ก ๋์ฒดํ์ฌ ์คํ ์๋ฃ๋ฅผ ์ ๋ฌํ  ๊ฒ์ด๊ณ , Undo/Redo์ ๊ด๋ จํ ์ด๋ฒคํธ๋ UI ํด๋ฐ ์ํ๊ฐ ๊ด๋ฆฌ๋ฅผ ์ํ์ฌ ๊ธฐ์กด๊ณผ ๊ฐ์ด UI๋ก ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ๋ค.
๋จ๋ UI ์ด๋ฒคํธ๋ 5๊ฐ์ด๋ค. objectActivated, objectStyleChanged, mousedown, redoStackChanged, undoStackChanged
| Name | Purpose | 
|---|---|
| addText | when mousedown event occurs in 'TEXT' drawing mode | 
| objectActivated | when user selects an object | 
| objectMoved | when user drags an object | 
| objectScaled | when object is being scaled | 
| textEditing | when textbox is being edited | 
| mousedown | just mousedown | 
| undoStackChanged | undo change event | 
| redoStackChanged | redo change event | 
/*
{
    id: number
    type: type
    left: number,
    top: number,
    width: number,
    fill: string
    stroke: string
    strokeWidth: number
    opacity: number,
    // text object
    text: string,
    fontFamily: string,
    fontSize: number,
    fontStyle: string,
    fontWeight: string,
    textAlign: string,
    textDecoration: string
}
*/
imageEditor.on('objectActivated', function(props) {
    console.log(props);
    console.log(props.type);
    console.log(props.id);
});class ServiceUI {
  -ImageEditor _imageEditor
}
class ImageEditor {
  -Invoker _invoker
  -Graphics _graphics
ย ย -void execute(commandName)
}
package "Middle Layer" #DDDDDD {
  class Invoker
  class Command
  class CommandFactory
}
together {
  class Invoker
  class Command
  class CommandFactory
}
package "Graphics Layer" #DDDDDD {
  class Graphics
}
package "Component" {
  class Component
  class Cropper
  class Filter
  class Flip
  class FreeDrawing
  class Icon
  class ImageLoader
  class Line
  class Rotation
  class Shape
  class Text
}
package "DrawingMode" {
  class DrawingMode
  class CropperDrawingMode
  class FreeDrawingMode
  class LineDrawingMode
  class ShapeDrawingMode
  class TextDrawingMode
}
class Invoker {
  -Array _undoStack
  -Array _redoStack
  +Promise execute("commandName")
  +Promise undo()
  +Promise redo()
  +void pushUndoStack(command, isSilent)
  +void pushRedoStack(command, isSilent)
}
class CommandFactory {
  -Map _commands
  +void register(commandObject)
  +Command create("commandName", ...args)
}
class Command {
  +string name
ย ย +Array args
  +Object _undoData
  +Promise execute()
  +Promise undo()
  +Command setExecuteCallback(callback)
  +Command setUndoCallback(callback)
}
class Graphics {
  -DrawingMode[] _drawingModeInstances
  -Component[] _components
  -Fabric.Canvas _canvas
  +setDrawingMode("modeName")
  +setDefaultPathStyle(style)
  +on("eventName", callback)
}
class Component {
}
class DrawingMode {
}
ServiceUI o-- ImageEditor
ImageEditor o-- Graphics
ImageEditor o-- Invoker
ImageEditor <|-- tui.util.CustomEvents
ImageEditor --> CommandFactory
ImageEditor --> Command
Invoker <|-- tui.util.CustomEvents
Invoker --> CommandFactory
Invoker o-- Command
Command o-- Graphics
Command o-- Component
Graphics o-- DrawingMode
Graphics o-- Component
Graphics o-- Fabric.Canvas
Graphics <|-- tui.util.CustomEvents
Component <|-- Cropper
Component <|-- Filter
Component <|-- Flip
Component <|-- FreeDrawing
Component <|-- Icon
Component <|-- ImageLoader
Component <|-- Line
Component <|-- Rotation
Component <|-- Shape
Component <|-- Text
Cropper <-- Fabric.Canvas
Filter <-- Fabric.Canvas
Flip <-- Fabric.Canvas
FreeDrawing <-- Fabric.Canvas
Icon <-- Fabric.Canvas
ImageLoader <-- Fabric.Canvas
Line <-- Fabric.Canvas
Rotation <-- Fabric.Canvas
Shape <-- Fabric.Canvas
Text <-- Fabric.Canvas
DrawingMode --> Component
DrawingMode <|-- CropperDrawingMode
DrawingMode <|-- FreeDrawingMode
DrawingMode <|-- LineDrawingMode
DrawingMode <|-- ShapeDrawingMode
DrawingMode <|-- TextDrawingMode
CropperDrawingMode <-- Cropper
FreeDrawingMode <-- FreeDrawing
LineDrawingMode <-- Line
ShapeDrawingMode <-- Shape
TextDrawingMode <-- Text