/qpaint

极客时间专栏《许式伟的架构课》相关的源代码:QPaint (画图程序)

Primary LanguageJavaScriptApache License 2.0Apache-2.0

QPaint (by Qiniu.com)

Build Status GoDoc

Qiniu Logo

辅助界面元素

接口规格 (第 31 讲)

类型 属性 方法 事件
BaseLineWidthPicker id: string
value: number
blur() onchange(event: Event)
BaseColorPicker id: string
value: Color
palette: string
blur() onchange(event: Event)
ColorPicker id: string
palette: string
value: Color
blur() onchange(event: Event)

QPaint DOM (第 30 讲)

网络协议

在 29 讲的基础上,我们增加了以下能力:

功能 请求包 返回包
同步变更 POST /drawings/<DrawingID>/sync
Content-Type: application/json

{
    "shapes": [<ShapeID1>, <ShapeID2>, <ShapeID3>, ...],
    "changes": [
        {
            "id": <ShapeID>,
            <Shape>
        },
        ...
    ]
}
200 OK

QPaint DOM (第 29 讲)

网络协议

功能 请求包 返回包
创建新drawing POST /drawings 200 OK
Content-Type: application/json

{
    "id": <DrawingID>
}
获得drawing GET /drawings/<DrawingID> 200 OK
Content-Type: application/json

{
    "shapes": [
        {
            "id": <ShapeID>
            <Shape>
        },
        ...
    ]
}
删除drawing DELETE /drawings/<DrawingID> 200 OK
创建新shape POST /drawings/<DrawingID>/shapes
Content-Type: application/json

{
    "id": <ShapeID>,
    <Shape>
}
200 OK
取得shape GET /drawings/<DrawingID>/shapes/<ShapeID> 200 OK
Content-Type: application/json

{
    <Shape>
}
修改shape POST /drawings/<DrawingID>/shapes/<ShapeID>
Content-Type: application/json

{
    <Shape>
}
200 OK
修改shape的顺序 POST /drawings/<DrawingID>/shapes/<ShapeID>
Content-Type: application/json

{
    "zorder": <ZorderOperation>
}
200 OK
删除shape DELETE /drawings/<DrawingID>/shapes/<ShapeID> 200 OK

其中 <Shape> 是这样的:

"path": {
    "points": [
        {"x": <X>, "y": <Y>},
        ...
    ],
    "close": <Boolean>,
    "style": <ShapeStyle>
}

或:

"line": {
    "pt1": {"x": <X>, "y": <Y>},
    "pt2": {"x": <X>, "y": <Y>},
    "style": <ShapeStyle>
}

或:

"rect": {
    "x": <X>,
    "y": <Y>,
    "width": <Width>,
    "height": <Height>,
    "style": <ShapeStyle>
}

或:

"ellipse": {
    "x": <X>,
    "y": <Y>,
    "radiusX": <RadiusX>,
    "radiusY": <RadiusY>,
    "style": <ShapeStyle>
}

其中 <ShapeStyle> 是这样的:

{
    "lineWidth": <Width>,  // 线宽
    "lineColor": <Color>,  // 线型颜色
    "fillColor": <Color>   // 填充色
}

其中 <ZorderOperation> 可能的值为:

  • "top": 到最顶
  • "bottom": 到最底
  • "front": 往前一层
  • "back": 往后一层

QPaint Web (第 27 讲)

Session-based Model

interface Shape {
    onpaint(ctx: CanvasRenderingContext2D): void
    bound(): Rect
    hitTest(pt: Point): {hitCode: number, hitShape: Shape}
    setProp(key: string, val: any): void
    move(dx, dy: number): void
}
类型 View Controllers
QPaintDoc onpaint(ctx) addShape(shape)
deleteShape(shape)
hitTest(pt)
QLine
QRect
QEllipse
QPath
onpaint(ctx) new QLine(pt1, pt2, style)
new QRect(rect, style)
new QEllipse(x, y, radiusX, radiusY, style)
new QPath(points, close, style)
bound()
hitTest(pt)
setProp(key, val)
move(dx, dy)
QShapeStyle new QShapeStyle(
  lineWidth, lineColor, fillColor
)
setProp(key, val)
clone()

ViewModel

interface Controller {
  stop(): void
  onpaint(ctx: CanvasRenderingContext2D): void
}
类型 Model View Controllers
数据 doc: QPaintDoc style: QShapeStyle
drawing: DOMElement
controllers: map[string]Controller
方法 - invalidateRect(rect) get currentKey()
get selection()
set selection(shape)
getMousePos(event)
registerController(name, ctrl)
invokeController(name)
stopController()
fireControllerReset()
事件 - onpaint(ctx) onmousedown(event)
onmousemove(event)
onmouseup(event)
ondblclick(event)
onkeydown(event)
onSelectionChanged(old)
onControllerReset()

Controllers

类型 Event Model View
Menu onControllerReset() - controllers: map[string]Controller
get currentKey()
invokeController(name)
PropSelectors onSelectionChanged(old) shape.style
shape.setProp(key, val)
style.clone()
style: QShapeStyle
get selection()
invalidateRect(rect)
MousePosTracker onmousemove - getMousePos(event)
QShapeSelector onmousedown(event)
onmousemove(event)
onmouseup(event)
onkeydown(event)
onpaint(ctx)
doc.deleteShape(shape)
doc.hitTest(pt)
shape.move(dx, dy)
shape.bound()
get selection()
set selection(shape)
getMousePos(event)
invalidateRect(rect)
registerController(name, ctrl)
QPathCreator onmousedown(event)
onmousemove(event)
onmouseup(event)
ondblclick(event)
onkeydown(event)
onpaint(ctx)
new QPath(points, close, style)
doc.addShape(shape)
getMousePos(event)
invalidateRect(rect)
registerController(name, ctrl)
fireControllerReset()
QFreePathCreator onmousedown(event)
onmousemove(event)
onmouseup(event)
onkeydown(event)
onpaint(ctx)
new QPath(points, close, style)
doc.addShape(shape)
getMousePos(event)
invalidateRect(rect)
registerController(name, ctrl)
fireControllerReset()
QRectCreator onmousedown(event)
onmousemove(event)
onmouseup(event)
onkeydown(event)
onpaint(ctx)
new QLine(pt1, pt2, style)
new QRect(rect, style)
new QEllipse(x, y, radiusX, radiusY, style)
doc.addShape(shape)
getMousePos(event)
invalidateRect(rect)
registerController(name, ctrl)
fireControllerReset()

Change Notes

Session-based Model

类型 View Controllers 修改说明
QPaintDoc - deleteShape(shape)
hitTest(pt)
- 增加 hitTest (确定鼠标点中哪个图形)、deleteShape (删除某个图形),都用于 QShapeSelector
QLine
QRect
QEllipse
QPath
- new QLine(pt1, pt2, style)
new QRect(rect, style)
new QEllipse(x, y, radiusX, radiusY, style)
new QPath(points, close, style)
bound()
hitTest(pt)
setProp(key, val)
move(dx, dy)
- 构造函数 style 参数由 QLineStyle 改为 QShapeStyle
- bound (求图形的外接矩形)、hitTest 用于选择图形
- setProp (修改图形样式的某个属性)
- move (移动图形)
QShapeStyle new QShapeStyle(
  lineWidth, lineColor, fillColor
)
setProp(key, val)
clone()
- QLineStyle 改名为 QShapeStyle
- 属性 width、color 改名为 lineWidth、lineColor
- 增加属性 fillColor (图形的填充色)
- 增加 setProp、clone (克隆图形样式)

ViewModel

类型 Model View Controllers 修改说明
数据 - style: QShapeStyle - - 属性 properties 改名为 style
方法 - - get selection()
set selection(shape)
fireControllerReset()
- 删除了 get lineStyle(),和 properties 统一为 style
- 增加了 selection 读写
- fireControllerReset,用于让创建图形的 Controller 完成或放弃图形创建时发出 onControllerReset 事件
事件 - - onSelectionChanged(old)
onControllerReset()
- onSelectionChanged 在被选择的图形改变时发出
- onControllerReset (见 fireControllerReset 的说明)

Controllers

类型 Event Model View 修改说明
Menu onControllerReset() - - - 引入了 v2 版本的切换 Controller 的范式,更接近现代的交互范式
PropSelectors onSelectionChanged(old) shape.style
shape.setProp(key, val)
style.clone()
style: QShapeStyle
get selection()
invalidateRect(rect)
- 这个 Controller 要比上一版本的复杂很多:之前只是修改 view 的 properties (现在是 style) 属性,以便于创建图形时引用。现在是改变它时还会作用于 selection (被选中的图形),改变它的样式;而且,在 selection 改变时,会自动更新界面以反映被选图形的样式
MousePosTracker - - - - 无变化
QShapeSelector onmousedown(event)
onmousemove(event)
onmouseup(event)
onkeydown(event)
onpaint(ctx)
doc.deleteShape(shape)
doc.hitTest(pt)
shape.move(dx, dy)
shape.bound()
get selection()
set selection(shape)
getMousePos(event)
invalidateRect(rect)
registerController(name, ctrl)
- 完全新增的 Controller
QPathCreator - new QPath(points, close, style) fireControllerReset() - QPath 的 style 参数从 QLineStyle 变为 QShapeStyle
- 完成或放弃图形创建时发出 onControllerReset 事件
QFreePathCreator - new QPath(points, close, style) fireControllerReset() - 同上
QRectCreator - new QLine(pt1, pt2, style)
new QRect(rect, style)
new QEllipse(x, y, radiusX, radiusY, style)
fireControllerReset() - 同上

QPaint Web (第 26 讲)

Session-based Model

interface Shape {
    onpaint(ctx: CanvasRenderingContext2D): void
}
类型 View Controllers
QPaintDoc onpaint(ctx) addShape(shape)
QLine
QRect
QEllipse
QPath
onpaint(ctx) new QLine(pt1, pt2, style)
new QRect(rect, style)
new QEllipse(x, y, radiusX, radiusY, style)
new QPath(points, close, style)
QLineStyle - new QLineStyle(width, color)

ViewModel

interface Controller {
  stop(): void
  onpaint(ctx: CanvasRenderingContext2D): void
}
类型 Model View Controllers
数据 doc: QPaintDoc properties: {
  lineWidth: number
  lineColor: string
}
drawing: DOMElement
controllers: map[string]Controller
方法 - invalidateRect(rect) get currentKey()
get lineStyle()
getMousePos(event)
registerController(name, ctrl)
invokeController(name)
stopController()
事件 - onpaint(ctx) onmousedown(event)
onmousemove(event)
onmouseup(event)
ondblclick(event)
onkeydown(event)

Controllers

类型 Event Model View
Menu - - controllers: map[string]Controller
get currentKey()
invokeController(name)
PropSelectors - - properties: {
  lineWidth: number
  lineColor: string
}
MousePosTracker onmousemove - getMousePos(event)
QPathCreator onmousedown(event)
onmousemove(event)
onmouseup(event)
ondblclick(event)
onkeydown(event)
onpaint(ctx)
new QPath(points, close, style)
doc.addShape(shape)
getMousePos(event)
invalidateRect(rect)
registerController(name, ctrl)
QFreePathCreator onmousedown(event)
onmousemove(event)
onmouseup(event)
onkeydown(event)
onpaint(ctx)
new QPath(points, close, style)
doc.addShape(shape)
getMousePos(event)
invalidateRect(rect)
registerController(name, ctrl)
QRectCreator onmousedown(event)
onmousemove(event)
onmouseup(event)
onkeydown(event)
onpaint(ctx)
new QLine(pt1, pt2, style)
new QRect(rect, style)
new QEllipse(x, y, radiusX, radiusY, style)
doc.addShape(shape)
getMousePos(event)
invalidateRect(rect)
registerController(name, ctrl)