Skip to content

Instantly share code, notes, and snippets.

@bramblex
Last active August 12, 2017 05:08
Show Gist options
  • Select an option

  • Save bramblex/8973f5490766efe7c3e68966e2bc061d to your computer and use it in GitHub Desktop.

Select an option

Save bramblex/8973f5490766efe7c3e68966e2bc061d to your computer and use it in GitHub Desktop.
'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