Last active
August 12, 2017 05:08
-
-
Save bramblex/8973f5490766efe7c3e68966e2bc061d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 'use strict' | |
| import * as Konva from 'konva' | |
| type Events = | |
| 'mouseover' | 'mouseout' | 'mouseenter' | | |
| 'mouseleave' | 'mousemove' | 'mousedown' | | |
| 'mouseup' | 'wheel' | 'click' | 'dblclick' | | |
| 'dragstart' | 'dragmove' | 'dragend' | | |
| 'touchstart' | 'touchmove' | 'touchend' | | |
| 'tap' | 'dbltap' | 'dragstart' | 'dragmove' | | |
| 'dragend' | |
| type ObserverFunc<State, Node, AttrType> | |
| = (state: State, node: Node) => AttrType | |
| type ObserverKey<State> = keyof State | |
| type Observer<State, Node, AttrType> | |
| = [ObserverKey<State> | '@init', ObserverFunc<State, Node, AttrType>] | |
| | [(ObserverKey<State> | '@init')[], ObserverFunc<State, Node, AttrType>] | |
| | ObserverKey<State> | |
| type ExtendConfig<NodeConfig> = NodeConfig & { show: boolean } | |
| type ObserverConfig<State, Node, ExtendNodeConfig> | |
| = {[P in keyof ExtendNodeConfig]?: Observer<State, Node, ExtendNodeConfig[P]>} | |
| type EventHandler<State, Node> | |
| = (state: KonvaViewModel<State>, node: Node, event: any) => any | |
| type EventHandlerConfig<State, Node> | |
| = {[key in Events]?: EventHandler<State, Node>} | |
| interface KonvaViewNode<State, Node, NodeConfig> { | |
| KonvaNodeClass: any | |
| attributes: Partial<NodeConfig> | |
| observers?: ObserverConfig<State, Node, ExtendConfig<NodeConfig>> | |
| handlers?: EventHandlerConfig<State, Node> | |
| } | |
| type KonvaRectView<State> = KonvaViewNode<State, Konva.Rect, Konva.RectConfig> | |
| type KonvaCircleView<State> = KonvaViewNode<State, Konva.Circle, Konva.CircleConfig> | |
| type KonvaEllipseView<State> = KonvaViewNode<State, Konva.Ellipse, Konva.EllipseConfig> | |
| type KonvaWedgeView<State> = KonvaViewNode<State, Konva.Wedge, Konva.WedgeConfig> | |
| type KonvaLineView<State> = KonvaViewNode<State, Konva.Line, Konva.LineConfig> | |
| type KonvaSpriteView<State> = KonvaViewNode<State, Konva.Sprite, Konva.SpriteConfig> | |
| type KonvaImageView<State> = KonvaViewNode<State, Konva.Image, Konva.ImageConfig> | |
| type KonvaTextView<State> = KonvaViewNode<State, Konva.Text, Konva.TextConfig> | |
| type KonvaTextPathView<State> = KonvaViewNode<State, Konva.TextPath, Konva.TextPathConfig> | |
| type KonvaStarView<State> = KonvaViewNode<State, Konva.Star, Konva.StarConfig> | |
| type KonvaRingView<State> = KonvaViewNode<State, Konva.Ring, Konva.RingConfig> | |
| type KonvaArcView<State> = KonvaViewNode<State, Konva.Arc, Konva.ArcConfig> | |
| type KonvaPathView<State> = KonvaViewNode<State, Konva.Path, Konva.PathConfig> | |
| type KonvaRegularPolygonView<State> = KonvaViewNode<State, Konva.RegularPolygon, Konva.RegularPolygonConfig> | |
| type KonvaArrowView<State> = KonvaViewNode<State, Konva.Arrow, Konva.ArrowConfig> | |
| type KonvaGroupView<State> = KonvaViewNode<State, Konva.Group, Konva.ContainerConfig> & { children?: KonvaView<State>[] } | |
| export type KonvaView<State> | |
| = KonvaRectView<State> | |
| | KonvaCircleView<State> | |
| | KonvaEllipseView<State> | |
| | KonvaWedgeView<State> | |
| | KonvaLineView<State> | |
| | KonvaSpriteView<State> | |
| | KonvaImageView<State> | |
| | KonvaTextView<State> | |
| | KonvaTextPathView<State> | |
| | KonvaStarView<State> | |
| | KonvaRingView<State> | |
| | KonvaArcView<State> | |
| | KonvaPathView<State> | |
| | KonvaRegularPolygonView<State> | |
| | KonvaArrowView<State> | |
| | KonvaGroupView<State> | |
| type KonvaViewConfig<State, Node, NodeConfig> = { | |
| attributes?: Partial<NodeConfig>, | |
| observers?: ObserverConfig<State, Node, ExtendConfig<NodeConfig>>, | |
| handlers?: EventHandlerConfig<State, Node> | |
| } | |
| type KonvaGroupViewConfig<State> = | |
| Partial<KonvaViewConfig<State, Konva.Group, Konva.ContainerConfig>> & { children?: KonvaView<State>[] } | |
| export class KonvaViewItemCreator<State> { | |
| Rect({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Rect, Konva.RectConfig>): KonvaRectView<State> { | |
| return { KonvaNodeClass: Konva.Rect, attributes, observers, handlers } | |
| } | |
| Circle({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Circle, Konva.CircleConfig>): KonvaCircleView<State> { | |
| return { KonvaNodeClass: Konva.Circle, attributes, observers, handlers } | |
| } | |
| Ellipse({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Ellipse, Konva.EllipseConfig>): KonvaEllipseView<State> { | |
| return { KonvaNodeClass: Konva.Ellipse, attributes, observers, handlers } | |
| } | |
| Wedge({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Wedge, Konva.WedgeConfig>): KonvaWedgeView<State> { | |
| return { KonvaNodeClass: Konva.Wedge, attributes, observers, handlers } | |
| } | |
| Line({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Line, Konva.LineConfig>): KonvaLineView<State> { | |
| return { KonvaNodeClass: Konva.Line, attributes, observers, handlers } | |
| } | |
| Sprite({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Sprite, Konva.SpriteConfig>): KonvaSpriteView<State> { | |
| return { KonvaNodeClass: Konva.Sprite, attributes, observers, handlers } | |
| } | |
| Image({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Image, Konva.ImageConfig>): KonvaImageView<State> { | |
| return { KonvaNodeClass: Konva.Image, attributes, observers, handlers } | |
| } | |
| Text({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Text, Konva.TextConfig>): KonvaTextView<State> { | |
| return { KonvaNodeClass: Konva.Text, attributes, observers, handlers } | |
| } | |
| TextPath({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.TextPath, Konva.TextPathConfig>): KonvaTextPathView<State> { | |
| return { KonvaNodeClass: Konva.TextPath, attributes, observers, handlers } | |
| } | |
| Star({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Star, Konva.StarConfig>): KonvaStarView<State> { | |
| return { KonvaNodeClass: Konva.Star, attributes, observers, handlers } | |
| } | |
| Ring({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Ring, Konva.RingConfig>): KonvaRingView<State> { | |
| return { KonvaNodeClass: Konva.Ring, attributes, observers, handlers } | |
| } | |
| Arc({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Arc, Konva.ArcConfig>): KonvaArcView<State> { | |
| return { KonvaNodeClass: Konva.Arc, attributes, observers, handlers } | |
| } | |
| Path({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Path, Konva.PathConfig>): KonvaPathView<State> { | |
| return { KonvaNodeClass: Konva.Path, attributes, observers, handlers } | |
| } | |
| RegularPolygon({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.RegularPolygon, Konva.RegularPolygonConfig>): KonvaRegularPolygonView<State> { | |
| return { KonvaNodeClass: Konva.RegularPolygon, attributes, observers, handlers } | |
| } | |
| Arrow({ attributes = {}, observers, handlers }: KonvaViewConfig<State, Konva.Arrow, Konva.ArrowConfig>): KonvaArrowView<State> { | |
| return { KonvaNodeClass: Konva.Arrow, attributes, observers, handlers } | |
| } | |
| Group({ attributes = {}, observers, handlers, children }: KonvaGroupViewConfig<State>): KonvaGroupView<State> { | |
| return { KonvaNodeClass: Konva.Group, attributes, observers, handlers, children } | |
| } | |
| } | |
| // 为了方便, 所以内部实现就不带具体的类型约束了,不然太累 | |
| export class KonvaViewModel<State> { | |
| private readonly node: Konva.Node | |
| private state: State = <State>{} | |
| private readonly observers_map: { | |
| [propNames: string]: [any, string, Function][] | |
| } = {} | |
| private _createNode_(view: KonvaView<State>): Konva.Node { | |
| const { KonvaNodeClass, attributes, observers, handlers } = view | |
| const node = <Konva.Node>(new KonvaNodeClass(attributes)) | |
| if (observers) { | |
| for (const attr of Object.getOwnPropertyNames(observers)) { | |
| const observer = (<any>observers)[attr] | |
| if (typeof observer === 'string') { | |
| if (!this.observers_map[observer]) { this.observers_map[observer] = [] } | |
| this.observers_map[observer].push([node, attr, (state) => state[observer]]) | |
| } else { | |
| const [events, func] = observer | |
| for (const event of Array.isArray(events) ? events : [events]) { | |
| if (!this.observers_map[event]) { this.observers_map[event] = [] } | |
| this.observers_map[event].push([node, attr, func]) | |
| } | |
| } | |
| } | |
| } | |
| if (handlers) { | |
| for (const name of Object.getOwnPropertyNames(handlers)) { | |
| const handler = (<any>handlers)[name] | |
| node.on(name, (evt: any) => handler(this, node, evt)) | |
| } | |
| } | |
| const children: KonvaView<State>[] = (<any>view).children | |
| if (children) { | |
| for (const child of children) { | |
| (<Konva.Container>node).add(this._createNode_(child)) | |
| } | |
| } | |
| return node | |
| } | |
| private _emit_(event: string) { | |
| const observers = this.observers_map[event] || [] | |
| for (const [node, attr, func] of observers) { | |
| if (attr === 'show') { | |
| func(this.state) ? node.show() : node.hide() | |
| } else { | |
| node[attr](func(this.state, node)) | |
| } | |
| } | |
| } | |
| constructor(init_state: State, view: KonvaView<State>) { | |
| this.node = this._createNode_(view) | |
| this.setState(init_state) | |
| this._emit_('@init') | |
| } | |
| setState(state: Partial<State>) { | |
| const names: string[] = [] | |
| for (const name of Object.getOwnPropertyNames(state)) { | |
| if ((<any>this.state)[name] !== (<any>state)[name]) { | |
| (<any>this.state)[name] = (<any>state)[name] | |
| names.push(name) | |
| } | |
| } | |
| for (const name of names) { | |
| this._emit_(name) | |
| } | |
| } | |
| getState(): State { return this.state } | |
| moveToLayer(layer: Konva.Layer) { this.node.moveTo(layer) } | |
| destroy() { this.node.remove && this.node.destroy() } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment