From 8cdff3a8e4e1b244bc258216d327a1233e3d23f8 Mon Sep 17 00:00:00 2001 From: "pengfei.zhou" Date: Wed, 24 Jul 2019 20:40:25 +0800 Subject: [PATCH 1/2] snake demo --- js-framework/demo.ts | 127 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 119 insertions(+), 8 deletions(-) diff --git a/js-framework/demo.ts b/js-framework/demo.ts index 4fb29bec..1fe4c7d2 100644 --- a/js-framework/demo.ts +++ b/js-framework/demo.ts @@ -2,7 +2,6 @@ import { StackConfig, ViewHolder, VMPanel, View, ViewModel, WRAP_CONTENT, Gravit interface CountModel { count: number - add: () => void } class CounterView extends ViewHolder { @@ -38,13 +37,15 @@ class CounterView extends ViewHolder { } class CounterVM extends ViewModel { + binding(v: CounterView, model: CountModel): void { - v.setCounter(model.add) v.setNumber(model.count) + v.setCounter(() => { + this.getModel().count++ + }) } } - class MyPage extends VMPanel{ getVMClass() { @@ -74,10 +75,120 @@ class MyPage extends VMPanel{ } +type Postion = { x: number, y: number } + +enum Direction { + left = 0, + right = 1, + up = 2, + down = 3, +} + +interface SnakeNode { + prev?: SnakeNode + next?: SnakeNode + x: number + y: number +} + +enum State { + idel, + run, + fail, +} + +class SnakeModel { + state = State.idel + direction = Direction.left + width = 10 + height = 10 + + food = { x: 0, y: 0 } + + head: SnakeNode = { + x: 0, + y: 0, + } + + refreshFood() { + this.food.x = Math.floor(Math.random() * (this.width - 1)) + this.food.y = Math.floor(Math.random() * (this.height - 1)) + } + + get tail() { + let node = this.head + while (node.next !== undefined) { + node = node.next + } + return node + } + get score() { + let node = this.head + let n = 0 + while (node.next !== undefined) { + n++ + node = node.next + } + return n + } + + forward(node: SnakeNode) { + switch (this.direction) { + case Direction.left: + node.x -= 10 + break; + case Direction.right: + node.x += 10 + break; + case Direction.up: + node.y -= 10 + break; + case Direction.down: + node.y += 10 + break; + } + } -class Snake { + step() { + if (this.state !== State.run) { + return + } + let tail = this.tail + while (tail.prev != undefined) { + tail.x = tail.prev.x + tail.y = tail.prev.y + tail = tail.prev + } + this.forward(this.head) + if (this.head.x < 0 || this.head.x >= this.width + || this.head.y < 0 || this.head.y >= this.height) { + //If out of bound + this.state = State.fail + } else if (this.head.x == this.food.x && this.head.y == this.food.y) { + //If eat food + let head = { x: this.food.x, y: this.food.y, next: this.head } + this.head.prev = head + this.forward(head) + this.head = head + this.refreshFood() + } + if (this.crashAtSelf()) { + //If crash at self + this.state = State.fail + } + } + crashAtSelf() { + let cur = this.head.next + while (cur) { + if (cur.x == this.head.x && cur.y == this.head.y) { + return true + } + cur = cur.next + } + return false + } } class SnakeView extends ViewHolder { @@ -98,19 +209,19 @@ class SnakeView extends ViewHolder { } } -class SnakeVM extends ViewModel{ - binding(v: SnakeView, model: Snake) { +class SnakeVM extends ViewModel{ + binding(v: SnakeView, model: SnakeModel) { } } @Entry -class SnakePanel extends VMPanel{ +class SnakePanel extends VMPanel{ getVMClass() { return SnakeVM } getModel() { - return new Snake + return new SnakeModel } getViewHolder() { From cd85a5a676c8c7bd83d1471f12cb48fe5d5d0d9c Mon Sep 17 00:00:00 2001 From: "pengfei.zhou" Date: Thu, 25 Jul 2019 00:14:55 +0800 Subject: [PATCH 2/2] snake demo --- js-framework/demo.ts | 57 ++++++++++++++++++++++++++++++++++--- js-framework/src/ui/view.ts | 5 ++-- js-framework/src/vm/mvvm.ts | 3 +- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/js-framework/demo.ts b/js-framework/demo.ts index 1fe4c7d2..47318bf1 100644 --- a/js-framework/demo.ts +++ b/js-framework/demo.ts @@ -100,9 +100,14 @@ enum State { class SnakeModel { state = State.idel direction = Direction.left - width = 10 - height = 10 + width: number + height: number + + constructor(w: number, h: number) { + this.width = w + this.height = h + } food = { x: 0, y: 0 } head: SnakeNode = { @@ -192,6 +197,7 @@ class SnakeModel { } class SnakeView extends ViewHolder { + panel: Stack = new Stack build(root: Group): void { root.bgColor = Color.parse('#000000') @@ -205,23 +211,66 @@ class SnakeView extends ViewHolder { top: 20 } } as StackConfig + root.addChild(this.panel) root.addChild(title) } } class SnakeVM extends ViewModel{ - binding(v: SnakeView, model: SnakeModel) { + timerId?: any + start() { + if (this.timerId !== undefined) { + clearInterval(this.timerId) + } + this.timerId = setInterval(() => { + this.getModel().step() + }, 1000) + } + + stop() { + if (this.timerId !== undefined) { + clearInterval(this.timerId) + this.timerId = undefined + } + } + + binding(v: SnakeView, model: SnakeModel) { + v.panel.width = model.width * 10 + v.panel.height = model.height * 10 + let node: SnakeNode | undefined = model.head + let nodes: SnakeNode[] = [] + while (node != undefined) { + nodes.push(node) + node = node.next + } + nodes.forEach((e, index) => { + let item = v.panel.children[index] + if (item) { + item.x = e.x * 10 + item.height = e.y * 10 + } else { + item = new Stack + item.bgColor = Color.parse('#0000ff') + item.width = item.height = 10 + v.panel.addChild(item) + } + }) + if (nodes.length < v.panel.children.length) { + v.panel.children.length = nodes.length + } } } @Entry class SnakePanel extends VMPanel{ + getVMClass() { return SnakeVM } + getModel() { - return new SnakeModel + return new SnakeModel(this.getRootView().width / 10, this.getRootView().width / 10) } getViewHolder() { diff --git a/js-framework/src/ui/view.ts b/js-framework/src/ui/view.ts index af7ffc48..0466eb06 100644 --- a/js-framework/src/ui/view.ts +++ b/js-framework/src/ui/view.ts @@ -69,7 +69,7 @@ export abstract class View implements Modeling { set: (target, p, v, receiver) => { const oldV = Reflect.get(target, p, receiver) const ret = Reflect.set(target, p, v, receiver) - if (Reflect.getMetadata(p, target)) { + if (Reflect.getMetadata(p, target) && oldV !== v) { receiver.onPropertyChanged(p.toString(), oldV, v) } return ret @@ -209,7 +209,7 @@ export interface LinearConfig extends Config { export abstract class Group extends View { @Property - children: View[] = new Proxy([], { + readonly children: View[] = new Proxy([], { set: (target, index, value) => { if (index === 'length') { this.getDirtyChildrenModel().length = value as number @@ -231,6 +231,7 @@ export abstract class Group extends View { addChild(view: View) { this.children.push(view) } + clean() { this.children.forEach(e => { e.clean() }) super.clean() diff --git a/js-framework/src/vm/mvvm.ts b/js-framework/src/vm/mvvm.ts index d0e2f2e2..78eea0ce 100644 --- a/js-framework/src/vm/mvvm.ts +++ b/js-framework/src/vm/mvvm.ts @@ -30,7 +30,7 @@ export abstract class ViewHolder { export abstract class VMPanel extends Panel { - private vm: ViewModel = new (this.getVMClass())(this.getModel(), this.getViewHolder()) + private vm?: ViewModel abstract getVMClass(): new (m: M, v: V) => ViewModel @@ -44,6 +44,7 @@ export abstract class VMPanel extends Pa } build(root: Group): void { + this.vm = new (this.getVMClass())(this.getModel(), this.getViewHolder()) this.vm.build(root) } }