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 (画图程序)
- Host: GitHub
- URL: https://github.com/qiniu/qpaint
- Owner: qiniu
- License: apache-2.0
- Created: 2019-07-16T18:05:04.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2020-04-04T19:34:21.000Z (about 5 years ago)
- Last Synced: 2025-05-05T20:17:26.338Z (about 2 months ago)
- Topics: architecture, go, golang, javascript, mvc-pattern
- Language: JavaScript
- Homepage:
- Size: 344 KB
- Stars: 179
- Watchers: 12
- Forks: 86
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# QPaint (by Qiniu.com)
[](https://travis-ci.org/qiniu/qpaint) [](https://godoc.org/github.com/qiniu/qpaint)
[](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) |