# qpaint **Repository Path**: gaozy2021/qpaint ## Basic Information - **Project Name**: qpaint - **Description**: 极客时间专栏《许式伟的架构课》相关的源代码:QPaint (画图程序) - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-06-04 - **Last Updated**: 2021-09-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## 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) |