An open API service indexing awesome lists of open source software.

https://github.com/qiniu/qpaint

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

architecture go golang javascript mvc-pattern

Last synced: about 2 months ago
JSON representation

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

Awesome Lists containing this project

README

        

# QPaint (by Qiniu.com)

[![Build Status](https://travis-ci.org/qiniu/qpaint.svg?branch=master)](https://travis-ci.org/qiniu/qpaint) [![GoDoc](https://godoc.org/github.com/qiniu/qpaint?status.svg)](https://godoc.org/github.com/qiniu/qpaint)

[![Qiniu Logo](http://open.qiniudn.com/logo.png)](http://www.qiniu.com/)

## 辅助界面元素

### 接口规格 (第 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/``/sync
Content-Type: `application/json`

{
    "shapes": [``, ``, ``, ...],
    "changes": [
        {
            "id": ``,
            ``
        },
        ...
    ]
} | 200 OK |

## QPaint DOM (第 29 讲)

### 网络协议

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

{
    "id": ``
} |
| 获得drawing | GET /drawings/`` | 200 OK
Content-Type: `application/json`

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

{
    "id": ``,
    ``
} | 200 OK |
| 取得shape | GET /drawings/``/shapes/`` | 200 OK
Content-Type: `application/json`

{
    ``
} |
| 修改shape | POST /drawings/``/shapes/``
Content-Type: `application/json`

{
    ``
} | 200 OK |
| 修改shape的顺序 | POST /drawings/``/shapes/``
Content-Type: `application/json`

{
    "zorder": ``
} | 200 OK |
| 删除shape | DELETE /drawings/``/shapes/`` | 200 OK |

其中 `` 是这样的:

```
"path": {
"points": [
{"x": , "y": },
...
],
"close": ,
"style":
}
```
或:
```
"line": {
"pt1": {"x": , "y": },
"pt2": {"x": , "y": },
"style":
}
```
或:
```
"rect": {
"x": ,
"y": ,
"width": ,
"height": ,
"style":
}
```
或:
```
"ellipse": {
"x": ,
"y": ,
"radiusX": ,
"radiusY": ,
"style":
}
```

其中 `` 是这样的:

```
{
"lineWidth": , // 线宽
"lineColor": , // 线型颜色
"fillColor": // 填充色
}
```

其中 `` 可能的值为:

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

## QPaint Web (第 27 讲)

### Session-based Model

* [dom.js](https://github.com/qiniu/qpaint/blob/v27/paintweb/www/dom.js)

```TypeScript
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

* [view.js](https://github.com/qiniu/qpaint/blob/v27/paintweb/www/view.js)
* [index.htm](https://github.com/qiniu/qpaint/blob/v27/paintweb/www/index.htm)

```TypeScript
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

* Menu, PropSelectors, MousePosTracker: [accel/menu.js](https://github.com/qiniu/qpaint/blob/v27/paintweb/www/accel/menu.js)
* ShapeSelector: [accel/select.js](https://github.com/qiniu/qpaint/blob/v27/paintweb/www/accel/select.js)
* Create Path: [creator/path.js](https://github.com/qiniu/qpaint/blob/v27/paintweb/www/creator/path.js)
* Create FreePath: [creator/freepath.js](https://github.com/qiniu/qpaint/blob/v27/paintweb/www/creator/freepath.js)
* Create Line, Rect, Ellipse, Circle: [creator/rect.js](https://github.com/qiniu/qpaint/blob/v27/paintweb/www/creator/rect.js)

| 类型 | 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

* https://github.com/qiniu/qpaint/compare/v26...v27

#### 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

* [dom.js](https://github.com/qiniu/qpaint/blob/v26/paintweb/www/dom.js)

```TypeScript
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

* [view.js](https://github.com/qiniu/qpaint/blob/v26/paintweb/www/view.js)
* [index.htm](https://github.com/qiniu/qpaint/blob/v26/paintweb/www/index.htm)

```TypeScript
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

* Menu, PropSelectors, MousePosTracker: [accel/menu.js](https://github.com/qiniu/qpaint/blob/v26/paintweb/www/accel/menu.js)
* Create Path: [creator/path.js](https://github.com/qiniu/qpaint/blob/v26/paintweb/www/creator/path.js)
* Create FreePath: [creator/freepath.js](https://github.com/qiniu/qpaint/blob/v26/paintweb/www/creator/freepath.js)
* Create Line, Rect, Ellipse, Circle: [creator/rect.js](https://github.com/qiniu/qpaint/blob/v26/paintweb/www/creator/rect.js)

| 类型 | 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) |