Commit 82bc2481 authored by rockyl's avatar rockyl

Merge branch '1.1' into dev

# Conflicts:
#	src/core/Entity.ts
parents 5ff277b6 b063940e
/**
* Created by rockyl on 2018/11/23.
*/
import {Entity, traverse, traversePostorder} from "./Entity";
import {injectProp} from "../tools/utils";
import {setupContext as setupInteractContext} from "./context/InteractContext";
import {clear, ScaleMode, setupContext as setupRenderContext} from "./context/RenderContext";
import './requestAnimationFrame';
/**
* 创建引擎实例
* @param options
*/
export function createEngineInstance(options?) {
let instance = new ScillaEngine();
instance.setup(options);
}
/**
* 引擎类
*/
class ScillaEngine {
/**
* 默认配置
*/
options: any = {
fps: 60,
designWidth: 750,
designHeight: 1334,
scaleMode: ScaleMode.FIXED_WIDTH,
touchEnabled: true,
};
_flush: number = 0;
_currentFlush: number = 0;
tsStart: number;
tsLast: number;
lastFPS: number = 0;
private readonly root: Entity;
constructor() {
this.root = new Entity('root');
this.root._restrict();
}
setup(options?){
injectProp(this.options, options);
const {canvas, designWidth, designHeight, scaleMode, modifyCanvasSize, touchEnabled} = options;
let canvasElement = typeof canvas == 'object' ? canvas : document.getElementById(canvas);
setupInteractContext({
canvas: canvasElement,
touchHandler: {
onTouchBegin: this.onTouchBegin.bind(this),
onTouchMove: this.onTouchMove.bind(this),
onTouchEnd: this.onTouchEnd.bind(this),
},
touchEnabled,
});
setupRenderContext({
canvas: canvasElement,
designWidth,
designHeight,
scaleMode,
modifyCanvasSize,
});
}
/**
* 开始引擎
*/
start() {
this.root.enabled = true;
this.tsStart = Date.now();
this.startTick();
}
/**
* 暂停引擎
*/
pause() {
this.root.enabled = false;
this.stopTick();
}
/**
* 获取节点路径
* @param entity
*/
getEntityPath(entity?: Entity): string {
let path = '';
let current = entity || this.root;
while (current.parent) {
path = current.parent.children.indexOf(current) + (path.length > 0 ? '|' : '') + path;
current = current.parent;
}
return path;
}
/**
* 根据节点路径获取节点
* @param path
*/
getEntityByPath(path?: string): Entity {
let target = this.root;
if (path.length > 0) {
let arr = path.split('|');
for (let item of arr) {
target = target.children[item];
if (!target) {
target = null;
break;
}
}
}
return target;
}
/**
* 获取当前帧率
*/
get fps() {
return this.lastFPS;
}
/**
* 开始时钟
*/
private startTick() {
this._flush = 60 / this.options.fps - 1 >> 0;
if (this._flush < 0) {
this._flush = 0;
}
requestAnimationFrame(this.flush);
}
/**
* 停止时钟
*/
private stopTick() {
}
/**
* 时钟触发
*/
private flush(tsNow): void {
if (this._flush == 0) {
this.onFrameTick(tsNow);
} else {
if (this._currentFlush == 0) {
this.onFrameTick(tsNow);
this._currentFlush = this._flush;
} else {
this._currentFlush--;
}
}
requestAnimationFrame(this.flush);
}
nextTicks = [];
nextTick(func, tickCount = 1) {
this.nextTicks.push({func, tickCount});
}
private onFrameTick(tsNow) {
clear();
this.lastFPS = Math.floor(1000 / (tsNow - this.tsLast));
this.tsLast = tsNow;
const ts = tsNow - this.tsStart;
traverse(this.root, function (child) {
if (!child.isFree && child.enabled) {
child.onUpdate(ts);
} else {
return true;
}
}, -1, true, function (current) {
current.afterUpdate();
});
//const tsPass = Date.now() - tsNow;
for (let i = 0, li = this.nextTicks.length; i < li; i++) {
const item = this.nextTicks[i];
item.tickCount--;
if (item.tickCount <= 0) {
item.func(ts);
this.nextTicks.splice(i, 1);
i--;
li--;
}
}
}
/**
* 代理出来的onTouchBegin方法
* @param event
*/
private onTouchBegin(event) {
traversePostorder(this.root, function (child) {
return child.onInteract(0, event);
})
}
/**
* 代理出来的onTouchMove方法
* @param event
*/
private onTouchMove(event) {
traversePostorder(this.root, function (child) {
return child.onInteract(1, event);
})
}
/**
* 代理出来的onTouchEnd方法
* @param event
*/
private onTouchEnd(event) {
traversePostorder(this.root, function (child) {
return child.onInteract(2, event);
})
}
}
...@@ -33,6 +33,10 @@ export type script = any; ...@@ -33,6 +33,10 @@ export type script = any;
export interface Frame{ export interface Frame{
x?: number; x?: number;
y?: number; y?: number;
w?: number; width?: number;
h?: number; height?: number;
offX?: number;
offY?: number;
sourceW?: number;
sourceH?: number;
} }
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
*/ */
import HashObject from "./HashObject"; import HashObject from "./HashObject";
import {Entity,} from "./Entity"; import {Node,} from "./nodes/Node";
import {EngineConfig} from "../engine-config"; import {EngineConfig} from "../engine-config";
const interactiveMap = [ const interactiveMap = [
...@@ -17,9 +17,9 @@ const interactiveMap = [ ...@@ -17,9 +17,9 @@ const interactiveMap = [
*/ */
export class Component extends HashObject { export class Component extends HashObject {
/** /**
* 所依附的实体 * 所依附的节点
*/ */
entity: Entity; node: Node;
protected delayCallbacks = []; protected delayCallbacks = [];
//是否有效 //是否有效
...@@ -37,7 +37,7 @@ export class Component extends HashObject { ...@@ -37,7 +37,7 @@ export class Component extends HashObject {
set enabled(value: boolean) { set enabled(value: boolean) {
if (this._enabled !== value) { if (this._enabled !== value) {
this._enabled = value; this._enabled = value;
if (this.entity && this.entity.isActive) { if (this.node && this.node.isActive) {
if (!EngineConfig.editorMode) { if (!EngineConfig.editorMode) {
if (this._enabled) { if (this._enabled) {
this.onEnable(); this.onEnable();
...@@ -50,18 +50,18 @@ export class Component extends HashObject { ...@@ -50,18 +50,18 @@ export class Component extends HashObject {
} }
/** /**
* 装配实体 * 装配节点
* @param entity * @param node
*/ */
_setup(entity: Entity) { _setup(node: Node) {
this.entity = entity; this.node = node;
} }
/** /**
* 卸载实体 * 卸载节点
*/ */
_unSetup() { _unSetup() {
this.entity = null; this.node = null;
} }
/** /**
...@@ -111,12 +111,12 @@ export class Component extends HashObject { ...@@ -111,12 +111,12 @@ export class Component extends HashObject {
this.onEditorUpdate(t); this.onEditorUpdate(t);
} }
$afterUpdate() { $onRegression() {
this.afterUpdate(); this.onRegression();
} }
$afterEditorUpdate() { $onEditorRegression() {
this.afterEditorUpdate(); this.onEditorRegression();
} }
private invokeDelayCallback(t) { private invokeDelayCallback(t) {
...@@ -144,13 +144,13 @@ export class Component extends HashObject { ...@@ -144,13 +144,13 @@ export class Component extends HashObject {
} }
/** /**
* 当子实体的时钟更新结束后 * 当子节点的时钟更新结束后
*/ */
afterUpdate() { onRegression() {
} }
afterEditorUpdate() { onEditorRegression() {
} }
...@@ -273,7 +273,7 @@ export class Component extends HashObject { ...@@ -273,7 +273,7 @@ export class Component extends HashObject {
* @param params 参数 * @param params 参数
*/ */
broadcast(method, level = -1, ...params) { broadcast(method, level = -1, ...params) {
this.entity.broadcast(method, level, ...params); this.node.broadcast(method, level, ...params);
} }
/** /**
...@@ -283,6 +283,6 @@ export class Component extends HashObject { ...@@ -283,6 +283,6 @@ export class Component extends HashObject {
* @param params 参数 * @param params 参数
*/ */
bubbling(method, ...params) { bubbling(method, ...params) {
this.entity.bubbling(method, ...params); this.node.bubbling(method, ...params);
} }
} }
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* 引擎 * 引擎
*/ */
import {Entity, RootEntity} from "./Entity"; import {Node, RootNode} from "./nodes/Node";
import {injectProp} from "../tools/utils"; import {injectProp} from "../tools/utils";
import InteractContext from "./context/InteractContext"; import InteractContext from "./context/InteractContext";
import RenderContext, {ScaleMode} from "./context/RenderContext"; import RenderContext, {ScaleMode} from "./context/RenderContext";
...@@ -36,7 +36,7 @@ export class ScillaEngine { ...@@ -36,7 +36,7 @@ export class ScillaEngine {
*/ */
readonly dataCenterConfig: any = {}; readonly dataCenterConfig: any = {};
private _root: Entity; private _root: RootNode;
private _canvasElement: HTMLCanvasElement; private _canvasElement: HTMLCanvasElement;
private _flush = 0; private _flush = 0;
...@@ -139,7 +139,7 @@ export class ScillaEngine { ...@@ -139,7 +139,7 @@ export class ScillaEngine {
onUpdateScale: this.onUpdateScale.bind(this), onUpdateScale: this.onUpdateScale.bind(this),
}); });
this._root = new RootEntity(this); this._root = new RootNode(this);
this._root._restrict(); this._root._restrict();
} }
...@@ -163,19 +163,19 @@ export class ScillaEngine { ...@@ -163,19 +163,19 @@ export class ScillaEngine {
} }
/** /**
* 获取根Entity * 获取根Node
*/ */
get root(): Entity { get root(): Node {
return this._root; return this._root;
} }
/** /**
* 获取实体路径 * 获取节点路径
* @param entity * @param node
*/ */
getEntityPath(entity?: Entity): string { getNodePath(node?: Node): string {
let path = ''; let path = '';
let current = entity || this._root; let current = node || this._root;
while (current.parent) { while (current.parent) {
path = current.parent.children.indexOf(current) + (path.length > 0 ? '|' : '') + path; path = current.parent.children.indexOf(current) + (path.length > 0 ? '|' : '') + path;
current = current.parent; current = current.parent;
...@@ -184,10 +184,10 @@ export class ScillaEngine { ...@@ -184,10 +184,10 @@ export class ScillaEngine {
} }
/** /**
* 根据实体路径获取实体 * 根据节点路径获取节点
* @param path * @param path
*/ */
getEntityByPath(path?: string): Entity { getNodeByPath(path?: string): Node {
let target = this._root; let target = this._root;
if (path.length > 0) { if (path.length > 0) {
...@@ -308,7 +308,7 @@ export class ScillaEngine { ...@@ -308,7 +308,7 @@ export class ScillaEngine {
return true; return true;
} }
}, -1, true, function (current) { }, -1, true, function (current) {
current.$afterUpdate(); current.onRegression();
}); });
for (let i = 0, li = this.nextTicks.length; i < li; i++) { for (let i = 0, li = this.nextTicks.length; i < li; i++) {
...@@ -356,7 +356,7 @@ export class ScillaEngine { ...@@ -356,7 +356,7 @@ export class ScillaEngine {
} }
/** /**
* 射线测试获取实体 * 射线测试获取节点
* 注:暂时只用在编辑器内 * 注:暂时只用在编辑器内
*/ */
getEntitiesByRayTest(x, y, ignoreMask = true) { getEntitiesByRayTest(x, y, ignoreMask = true) {
...@@ -365,7 +365,7 @@ export class ScillaEngine { ...@@ -365,7 +365,7 @@ export class ScillaEngine {
let rendererDef = this.getDefByName('components/renderer/Renderer'); let rendererDef = this.getDefByName('components/renderer/Renderer');
traversePostorder(this._root, function (child) { traversePostorder(this._root, function (child) {
const transform = child.components[0]; const transform = child.components[0];
const matrix = transform['getMatrix'](true, true, true); const matrix = transform['getMatrix'](true, true);
matrix.transformPoint(x, y, localPos); matrix.transformPoint(x, y, localPos);
const renderers = child.getComponents(rendererDef); const renderers = child.getComponents(rendererDef);
......
...@@ -9,11 +9,11 @@ import {Frame} from "../ReType"; ...@@ -9,11 +9,11 @@ import {Frame} from "../ReType";
/** /**
* 图集 * 图集
*/ */
export class Sheet extends HashObject{ export class Sheet extends HashObject {
/** /**
* 图集原图 * 图集原图或原材质
*/ */
img: any; source: any;
/** /**
* 图集分割配置 * 图集分割配置
*/ */
...@@ -21,13 +21,22 @@ export class Sheet extends HashObject{ ...@@ -21,13 +21,22 @@ export class Sheet extends HashObject{
private _textureCache: any = {}; private _textureCache: any = {};
constructor(img?, frames?: Frame) { constructor(source?: HTMLImageElement | Texture, frames?) {
super(); super();
if(img){ this.init(source, frames);
this.img = img; }
/**
* 初始化
* @param source
* @param frames
*/
init(source?: HTMLImageElement | Texture, frames?) {
if (source) {
this.source = source;
} }
if(frames){ if (frames) {
this.frames = frames; this.frames = frames;
} }
} }
...@@ -47,7 +56,7 @@ export class Sheet extends HashObject{ ...@@ -47,7 +56,7 @@ export class Sheet extends HashObject{
* @param force * @param force
*/ */
generateTexture(name: string, force = false): Texture { generateTexture(name: string, force = false): Texture {
const {img, frames, _textureCache} = this; const {source, frames, _textureCache} = this;
if (!force && _textureCache[name]) { if (!force && _textureCache[name]) {
return _textureCache[name]; return _textureCache[name];
...@@ -55,7 +64,13 @@ export class Sheet extends HashObject{ ...@@ -55,7 +64,13 @@ export class Sheet extends HashObject{
const frame = frames[name]; const frame = frames[name];
if (frame) { if (frame) {
return _textureCache[name] = createTexture(img, frame); let img = source, realFrame = frame;
if(source instanceof Texture){
img = source.img;
realFrame.x += source.x;
realFrame.y += source.y;
}
return _textureCache[name] = createTexture(img, realFrame);
} }
} }
...@@ -64,7 +79,7 @@ export class Sheet extends HashObject{ ...@@ -64,7 +79,7 @@ export class Sheet extends HashObject{
* @param name * @param name
* @param texture * @param texture
*/ */
inputTexture(name: string, texture: Texture){ inputTexture(name: string, texture: Texture) {
this._textureCache[name] = texture; this._textureCache[name] = texture;
} }
...@@ -97,14 +112,29 @@ export class Sheet extends HashObject{ ...@@ -97,14 +112,29 @@ export class Sheet extends HashObject{
} }
/** /**
* 销毁自身 * 销毁材质
* @param name
*/ */
destroy() { destroyTexture(name){
this.img = null; this._textureCache[name].destroy();
delete this._textureCache[name];
}
/**
* 销毁所有材质
*/
cleanAllTexture(){
for (let key in this._textureCache) { for (let key in this._textureCache) {
this._textureCache[key].destroy(); this.destroyTexture(key);
delete this._textureCache[key];
} }
} }
/**
* 销毁自身
*/
destroy() {
this.source = null;
this.cleanAllTexture();
}
} }
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
* Created by rockyl on 2018/7/12. * Created by rockyl on 2018/7/12.
*/ */
import Bounds from "../support/Bounds";
import HashObject from "../core/HashObject"; import HashObject from "../core/HashObject";
import {createCanvas} from "./context/RenderContext"; import {createCanvas} from "./context/RenderContext";
import {Frame} from "../ReType"; import {Frame} from "../ReType";
import {dirtyFieldDetector} from "../tools/decorators"; import {dirtyFieldDetector, dirtyFieldTrigger} from "../tools/decorators";
import {utils} from "../tools";
/** /**
* 纹理类 * 纹理类
...@@ -15,7 +15,23 @@ export default class Texture extends HashObject { ...@@ -15,7 +15,23 @@ export default class Texture extends HashObject {
@dirtyFieldDetector @dirtyFieldDetector
img: any; img: any;
private _bounds: Bounds; @dirtyFieldTrigger
x: number;
@dirtyFieldTrigger
y: number;
@dirtyFieldTrigger
width: number;
@dirtyFieldTrigger
height: number;
@dirtyFieldTrigger
offX: number = 0;
@dirtyFieldTrigger
offY: number = 0;
@dirtyFieldTrigger
sourceW: number;
@dirtyFieldTrigger
sourceH: number;
private _cacheCanvas; private _cacheCanvas;
private _cacheContext; private _cacheContext;
...@@ -23,9 +39,6 @@ export default class Texture extends HashObject { ...@@ -23,9 +39,6 @@ export default class Texture extends HashObject {
super(); super();
this['isDirty'] = true; this['isDirty'] = true;
this._bounds = new Bounds(0, 0, 0, 0, () => {
this['isDirty'] = true;
});
} }
/** /**
...@@ -33,8 +46,13 @@ export default class Texture extends HashObject { ...@@ -33,8 +46,13 @@ export default class Texture extends HashObject {
* @param frame * @param frame
*/ */
setFrame(frame: Frame) { setFrame(frame: Frame) {
let {x, y, w, h} = frame; utils.injectProp(this, frame);
this._bounds.setTo(x, y, w, h); if (!frame.hasOwnProperty('sourceW')) {
this.sourceW = frame.width;
}
if (!frame.hasOwnProperty('sourceH')) {
this.sourceH = frame.height;
}
} }
/** /**
...@@ -45,27 +63,6 @@ export default class Texture extends HashObject { ...@@ -45,27 +63,6 @@ export default class Texture extends HashObject {
this.img = img; this.img = img;
} }
/**
* 获取纹理宽度
*/
get width() {
return this._bounds.width;
}
/**
* 获取纹理高度
*/
get height() {
return this._bounds.height;
}
/**
* 获取边界
*/
get bounds(): Bounds {
return this._bounds;
}
/** /**
* 产生一个缓存画布 * 产生一个缓存画布
*/ */
...@@ -75,7 +72,7 @@ export default class Texture extends HashObject { ...@@ -75,7 +72,7 @@ export default class Texture extends HashObject {
if (this['isDirty']) { if (this['isDirty']) {
this['isDirty'] = false; this['isDirty'] = false;
const {_bounds: {width, height}} = this; const {width, height} = this;
if (!canvas) { if (!canvas) {
canvas = this._cacheCanvas = createCanvas(); canvas = this._cacheCanvas = createCanvas();
...@@ -89,8 +86,6 @@ export default class Texture extends HashObject { ...@@ -89,8 +86,6 @@ export default class Texture extends HashObject {
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
this.drawToCanvas(context); this.drawToCanvas(context);
this.drawToCanvas(context);
} }
return canvas; return canvas;
...@@ -106,10 +101,12 @@ export default class Texture extends HashObject { ...@@ -106,10 +101,12 @@ export default class Texture extends HashObject {
* @param dw * @param dw
* @param dh * @param dh
*/ */
drawToCanvas(context, dx = 0, dy = 0, sx?, sy?, dw?, dh?) { drawToCanvas(context: CanvasRenderingContext2D, dx = 0, dy = 0, sx?, sy?, dw?, dh?) {
const {_bounds: {x, y, width, height}} = this; const {x, y, width, height, offX, offY} = this;
context.drawImage(this.img, sx || x, sy || y, width, height, dx, dy, dw || width, dh || height); console.log(x, y, width, height);
console.log(sx || x, sy || y, width, height, dx + offX, dy + offY, dw || width, dh || height);
context.drawImage(this.img, sx || x, sy || y, width, height, dx + offX, dy + offY, dw || width, dh || height);
} }
/** /**
...@@ -117,7 +114,6 @@ export default class Texture extends HashObject { ...@@ -117,7 +114,6 @@ export default class Texture extends HashObject {
*/ */
destroy() { destroy() {
this.img = null; this.img = null;
this._bounds = null;
this.destroyCacheCanvas(); this.destroyCacheCanvas();
} }
...@@ -138,7 +134,7 @@ export default class Texture extends HashObject { ...@@ -138,7 +134,7 @@ export default class Texture extends HashObject {
export function createTexture(img, frame?: Frame): Texture { export function createTexture(img, frame?: Frame): Texture {
const texture = new Texture(); const texture = new Texture();
texture.setImg(img); texture.setImg(img);
texture.setFrame(frame || {x: 0, y: 0, w: img.width, h: img.height}); texture.setFrame(frame || {x: 0, y: 0, width: img.width, height: img.height});
return texture; return texture;
} }
/**
* Created by rockyl on 2019-07-10.
*/
...@@ -105,8 +105,6 @@ export default class RenderContext { ...@@ -105,8 +105,6 @@ export default class RenderContext {
this.engine = engine; this.engine = engine;
} }
//todo
private onModify(value, key, oldValue) { private onModify(value, key, oldValue) {
if (!this.dirtyFieldTriggerLock) { if (!this.dirtyFieldTriggerLock) {
this.updateScaleModeSelf(); this.updateScaleModeSelf();
...@@ -348,7 +346,7 @@ export default class RenderContext { ...@@ -348,7 +346,7 @@ export default class RenderContext {
let {shortcutCanvas, stageWidth, stageHeight} = this; let {shortcutCanvas, stageWidth, stageHeight} = this;
if (!shortcutCanvas) { if (!shortcutCanvas) {
this.shortcutCanvas = createCanvas(); shortcutCanvas = this.shortcutCanvas = createCanvas();
} }
const sx = bounds ? bounds.x || 0 : 0; const sx = bounds ? bounds.x || 0 : 0;
const sy = bounds ? bounds.y || 0 : 0; const sy = bounds ? bounds.y || 0 : 0;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
*/ */
export {Component} from "./Component"; export {Component} from "./Component";
export {Entity} from './Entity' export {Node, RootNode} from './nodes/Node'
export {ScillaEvent} from './ScillaEvent' export {ScillaEvent} from './ScillaEvent'
export {createCanvas, ScaleMode} from './context/RenderContext'; export {createCanvas, ScaleMode} from './context/RenderContext';
export {default as engine, ScillaEngine} from './ScillaEngine' export {default as engine, ScillaEngine} from './ScillaEngine'
...@@ -13,3 +13,4 @@ export * from './Sheet' ...@@ -13,3 +13,4 @@ export * from './Sheet'
export * from './FrameAnimation' export * from './FrameAnimation'
export * from './interpreter' export * from './interpreter'
export * from './nodes'
...@@ -4,13 +4,13 @@ ...@@ -4,13 +4,13 @@
* 配置文件解释器 * 配置文件解释器
*/ */
import {Entity} from "../core/Entity"; import {Node} from "./nodes/Node";
import {ScillaEvent} from "../core/ScillaEvent"; import {ScillaEvent} from "../core/ScillaEvent";
import {EngineConfig} from "../engine-config"; import {EngineConfig} from "../engine-config";
import engine from "./ScillaEngine"; import engine from "./ScillaEngine";
import {traverse} from "./utils"; import {traverse} from "./utils";
let entityMap = {}; let nodeMap = {};
let prefabID: number = 0; let prefabID: number = 0;
let _getResProxy: Function; let _getResProxy: Function;
...@@ -24,89 +24,85 @@ export function setGetResProxy(func: Function) { ...@@ -24,89 +24,85 @@ export function setGetResProxy(func: Function) {
* @param root * @param root
* @param getResProxy * @param getResProxy
*/ */
export function setupScene(config: any, root: Entity, getResProxy: Function) { export function setupScene(config: any, root: Node, getResProxy: Function) {
setGetResProxy(getResProxy); setGetResProxy(getResProxy);
instantiateConfig(config, root); instantiateConfig(config, root);
} }
/**
* 清空实体
* @param entity
*/
export function cleanEntity(entity: Entity) {
entity.removeAllComponents();
entity.removeChildren();
}
/** /**
* 装配预制体 * 装配预制体
* @param config * @param config
*/ */
export function instantiate(config: any): Entity { export function instantiate(config: any): Node {
let pid = ++prefabID; let pid = ++prefabID;
const entity = instantiateConfig(config, null, pid); const node = instantiateConfig(config, null, pid);
return entity.children[0]; return node.children[0];
} }
/** /**
* 装配树实体 * 装配树节点
* @param config * @param config
* @param root * @param root
* @param pid * @param pid
*/ */
function instantiateConfig(config, root?: Entity, pid?: number): Entity { function instantiateConfig(config, root?: Node, pid?: number): Node {
let rootConfig = config.root; let rootConfig = config.root;
const entity = setupEntity(rootConfig, root, pid); const node = setupNode(rootConfig, root, pid);
setupComponent(rootConfig, entity, true); setupComponent(rootConfig, node, true);
injectComponent(rootConfig, entity, true, pid); injectComponent(rootConfig, node, true, pid);
traverse(entity, child => { traverse(node, child => {
child.active(); child.active();
return false; return false;
}); });
entityMap = {}; nodeMap = {};
return entity; return node;
} }
/** /**
* 装配实体 * 装配节点
* @param config * @param config
* @param root * @param root
* @param pid * @param pid
*/ */
function setupEntity(config, root?: Entity, pid?: number): Entity { function setupNode(config, root?: Node, pid?: number): Node {
let entity: Entity = null; let node: Node = null;
if (config) { if (config) {
let {name, uuid, children} = config; let {name, uuid, children, base} = config;
if (pid !== undefined && uuid !== undefined) { if (pid !== undefined && uuid !== undefined) {
uuid = pid + '_' + uuid; uuid = pid + '_' + uuid;
} }
entity = root || new Entity(name, uuid); if (root) {
node = root;
} else {
let temp = engine.getDefByName(base);
node = new temp(name, uuid);
}
entityMap[uuid] = entity; nodeMap[uuid] = node;
if (children) { if (children) {
for (let i = 0, li = children.length; i < li; i++) { for (let i = 0, li = children.length; i < li; i++) {
let child = children[i]; let child = children[i];
const childEntity = setupEntity(child, null, pid); const childNode = setupNode(child, null, pid);
entity.addChild(childEntity); node.addChild(childNode);
} }
} }
if (!root) { if (!root) {
entity.enabled = !config.disabled; node.enabled = !config.disabled;
} }
} }
return entity; return node;
} }
/** /**
...@@ -115,18 +111,18 @@ function setupEntity(config, root?: Entity, pid?: number): Entity { ...@@ -115,18 +111,18 @@ function setupEntity(config, root?: Entity, pid?: number): Entity {
* @param root * @param root
* @param includeSelf * @param includeSelf
*/ */
function setupComponent(config, root: Entity, includeSelf: boolean = false) { function setupComponent(config, root: Node, includeSelf: boolean = false) {
if (includeSelf) { if (includeSelf) {
instantiateComponents(root, config); instantiateComponents(root, config);
} }
if (config && config.children) { if (config && config.children) {
for (let i = 0, li = root.children.length; i < li; i++) { for (let i = 0, li = root.children.length; i < li; i++) {
const child = config.children[i]; const child = config.children[i];
const entity = root.children[i]; const node = root.children[i];
instantiateComponents(entity, child); instantiateComponents(node, child);
setupComponent(child, entity); setupComponent(child, node);
} }
} }
} }
...@@ -138,44 +134,46 @@ function setupComponent(config, root: Entity, includeSelf: boolean = false) { ...@@ -138,44 +134,46 @@ function setupComponent(config, root: Entity, includeSelf: boolean = false) {
* @param includeSelf * @param includeSelf
* @param pid * @param pid
*/ */
function injectComponent(config, root: Entity, includeSelf = false, pid?: number) { function injectComponent(config, root: Node, includeSelf = false, pid?: number) {
if (includeSelf) { if (includeSelf) {
injectComponents(root, config, pid); injectComponents(root, config, pid);
} }
if (config && config.children) { if (config && config.children) {
for (let i = 0, li = root.children.length; i < li; i++) { for (let i = 0, li = root.children.length; i < li; i++) {
const child = config.children[i]; const child = config.children[i];
const entity = root.children[i]; const node = root.children[i];
injectComponents(entity, child, pid); injectComponents(node, child, pid);
injectComponent(child, entity, false, pid); injectComponent(child, node, false, pid);
} }
} }
} }
/** /**
* 实例化组件列表 * 实例化组件列表
* @param entity * @param node
* @param config * @param config
*/ */
function instantiateComponents(entity: Entity, config: any) { function instantiateComponents(node: Node, config: any) {
if (config.components) { if (config.components) {
for (const component of config.components) { for (const component of config.components) {
instantiateComponent(entity, component); instantiateComponent(node, component);
} }
} }
} }
/** /**
* 注入组件列表参数 * 注入组件列表参数
* @param entity * @param node
* @param config * @param config
* @param pid * @param pid
*/ */
function injectComponents(entity: Entity, config: any, pid?: number) { function injectComponents(node: Node, config: any, pid?: number) {
injectComponentProperties(node, config, pid);
if (config.components) { if (config.components) {
const components = entity.components; const components = node.components;
for (let i = 0, li = config.components.length; i < li; i++) { for (let i = 0, li = config.components.length; i < li; i++) {
const component = config.components[i]; const component = config.components[i];
...@@ -200,10 +198,10 @@ export function injectComponentProperties(component, config, pid?: number) { ...@@ -200,10 +198,10 @@ export function injectComponentProperties(component, config, pid?: number) {
/** /**
* 实例化组件 * 实例化组件
* @param entity * @param node
* @param config * @param config
*/ */
export function instantiateComponent(entity: Entity, config: any) { export function instantiateComponent(node: Node, config: any) {
const {script,} = config; const {script,} = config;
let def = engine.getDefByName(script); let def = engine.getDefByName(script);
...@@ -214,7 +212,7 @@ export function instantiateComponent(entity: Entity, config: any) { ...@@ -214,7 +212,7 @@ export function instantiateComponent(entity: Entity, config: any) {
const instance: any = new def(); const instance: any = new def();
instance.enabled = !config.disabled; instance.enabled = !config.disabled;
entity.addComponent(instance); node.addComponent(instance);
return instance; return instance;
} }
...@@ -223,13 +221,13 @@ const skipKeys = ['_type_', '_constructor_']; ...@@ -223,13 +221,13 @@ const skipKeys = ['_type_', '_constructor_'];
/** /**
* 属性注入 * 属性注入
* @param entity * @param node
* @param propertiesConfig * @param propertiesConfig
* @param pid * @param pid
*/ */
function injectProperties(entity, propertiesConfig, pid?: number) { function injectProperties(node, propertiesConfig, pid?: number) {
if (!entity) { if (!node) {
console.warn('entity is null.'); console.warn('node is null.');
return; return;
} }
for (const key in propertiesConfig) { for (const key in propertiesConfig) {
...@@ -237,22 +235,22 @@ function injectProperties(entity, propertiesConfig, pid?: number) { ...@@ -237,22 +235,22 @@ function injectProperties(entity, propertiesConfig, pid?: number) {
continue; continue;
} }
const propertyOfConfig: any = propertiesConfig[key]; const propertyOfConfig: any = propertiesConfig[key];
let propertyOfInstance = entity[key]; let propertyOfInstance = node[key];
if (typeof propertyOfConfig === 'object') { if (typeof propertyOfConfig === 'object') {
if (propertyOfInstance instanceof ScillaEvent) { if (propertyOfInstance instanceof ScillaEvent) {
if (!EngineConfig.editorMode) { if (!EngineConfig.editorMode) {
injectEvent(propertyOfInstance, propertyOfConfig, pid); injectEvent(propertyOfInstance, propertyOfConfig, pid);
} }
} else if (propertyOfConfig._type_ === 'raw') { } else if (propertyOfConfig._type_ === 'raw') {
entity[key] = propertyOfInstance = propertyOfConfig.data; node[key] = propertyOfInstance = propertyOfConfig.data;
} else { } else {
if (Array.isArray(propertyOfConfig) && !propertyOfInstance) { if (Array.isArray(propertyOfConfig) && !propertyOfInstance) {
entity[key] = propertyOfInstance = [] node[key] = propertyOfInstance = []
} }
entity[key] = injectObject(propertyOfInstance, propertyOfConfig, pid); node[key] = injectObject(propertyOfInstance, propertyOfConfig, pid);
} }
} else { } else {
injectBaseType(entity, key, propertyOfConfig, pid); injectBaseType(node, key, propertyOfConfig, pid);
} }
} }
} }
...@@ -277,7 +275,7 @@ function injectObject(propertyOfInstance, propertyOfConfig, pid?: number) { ...@@ -277,7 +275,7 @@ function injectObject(propertyOfInstance, propertyOfConfig, pid?: number) {
return propertyOfInstance; return propertyOfInstance;
} }
function injectBaseType(entity, key, propertyOfConfig, pid?: number) { function injectBaseType(node, key, propertyOfConfig, pid?: number) {
let propertyValue; let propertyValue;
if (typeof propertyOfConfig === 'string') { if (typeof propertyOfConfig === 'string') {
propertyValue = getLink(propertyOfConfig, pid); propertyValue = getLink(propertyOfConfig, pid);
...@@ -291,23 +289,23 @@ function injectBaseType(entity, key, propertyOfConfig, pid?: number) { ...@@ -291,23 +289,23 @@ function injectBaseType(entity, key, propertyOfConfig, pid?: number) {
} }
if (typeof propertyValue === 'function') { if (typeof propertyValue === 'function') {
Object.defineProperty(entity, keyAvatar, { Object.defineProperty(node, keyAvatar, {
get() { get() {
return this[`func_${keyAvatar}`](entity, engine.dataCenter) return this[`func_${keyAvatar}`](node, engine.dataCenter)
}, },
set(v) { set(v) {
this[`func_${keyAvatar}`] = v; this[`func_${keyAvatar}`] = v;
} }
}) })
} }
entity[keyAvatar] = propertyValue; node[keyAvatar] = propertyValue;
} }
function injectEvent(event: ScillaEvent, config, pid?) { function injectEvent(event: ScillaEvent, config, pid?) {
for (const {entity: entityName, component: componentIndex, method: methodName, param} of config) { for (const {node: nodeName, component: componentIndex, method: methodName, param} of config) {
if (entityName && componentIndex >= 0 && methodName) { if (nodeName && componentIndex >= 0 && methodName) {
const entity = getLink(entityName, pid); const node = getLink(nodeName, pid);
const component = entity.components[componentIndex]; const component = node.components[componentIndex];
const method = component[methodName]; const method = component[methodName];
if (method) { if (method) {
if (param == undefined) { if (param == undefined) {
...@@ -325,9 +323,9 @@ function getLink(str: string, pid?: number) { ...@@ -325,9 +323,9 @@ function getLink(str: string, pid?: number) {
if (str.indexOf('res|') == 0) { //res uuid if (str.indexOf('res|') == 0) { //res uuid
const uuid = str.substr(4); const uuid = str.substr(4);
result = _getResProxy(uuid); result = _getResProxy(uuid);
} else if (str.indexOf('entity|') == 0) { //entity uuid } else if (str.indexOf('node|') == 0) { //node uuid
const uuid = transPrefabUUID(str.substr(7), pid); const uuid = transPrefabUUID(str.substr(7), pid);
result = entityMap[uuid]; result = nodeMap[uuid];
} else if (str.indexOf('dynamic|') == 0) { //dynamic } else if (str.indexOf('dynamic|') == 0) { //dynamic
const [_, type, expression] = str.split('|'); const [_, type, expression] = str.split('|');
result = () => { result = () => {
...@@ -346,10 +344,10 @@ function transPrefabUUID(uuid, pid: number) { ...@@ -346,10 +344,10 @@ function transPrefabUUID(uuid, pid: number) {
return pid ? pid + '_' + uuid : uuid; return pid ? pid + '_' + uuid : uuid;
} }
function wrapperScript(script){ function wrapperScript(script) {
try { try {
return new Function('component', 'dataCenter', script); return new Function('component', 'dataCenter', script);
}catch (e) { } catch (e) {
console.warn('script is correct :', script); console.warn('script is correct :', script);
} }
} }
/**
* Created by rockyl on 2019-07-05.
*
* (圆角)矩形
*/
import {GraphicNode} from "./GraphicNode";
import {dirtyFieldDetector} from "../../tools/decorators";
export class Circle extends GraphicNode {
/**
* 开始角度
*/
@dirtyFieldDetector
startAngle: number = 0;
/**
* 结束角度
*/
@dirtyFieldDetector
endAngle: number = 360;
/**
* 是否回归到圆心
*/
@dirtyFieldDetector
backToCenter: boolean = true;
/**
* @inheritDoc
*/
protected draw(context) {
const {bounds: {width, height}, startAngle, endAngle, backToCenter, _margin, _useCacheMode} = this;
let offset = _useCacheMode ? _margin : 0;
const radius = Math.min(width, height) / 2;
const pos = offset + radius;
if(startAngle == 0 && endAngle == 360){
context.arc(pos, pos, radius, 0, 2 * Math.PI);
}else{
if(backToCenter){
context.moveTo(pos, pos);
}
context.arc(pos, pos, radius, startAngle * Math.PI / 180, endAngle * Math.PI / 180);
if(backToCenter){
context.lineTo(pos, pos);
}
}
super.draw(context);
}
}
/**
* Created by rockyl on 2019-07-05.
*
* 绘制节点
*/
import {Node} from "./Node";
import {dirtyFieldDetector} from "../../tools/decorators";
import {color} from "../../ReType";
export class GraphicNode extends Node {
/**
* 填充颜色
*/
@dirtyFieldDetector
fillColor: color = '#42bce4';
/**
* 边框颜色
*/
@dirtyFieldDetector
borderColor: color = 'white';
/**
* 边框宽度
*/
@dirtyFieldDetector
borderWidth = 0;
/**
* 是否为mask
*/
@dirtyFieldDetector
isMask = false;
/**
* 是否显示mask
*/
@dirtyFieldDetector
maskVisible = false;
useCacheMode = true;
measureBounds() {
super.measureBounds();
this._margin = this.borderWidth;
}
protected getUseCacheMode(){
return this._useCacheMode && !this.isMask;
}
/**
* @inheritDoc
*/
protected beforeDraw() {
super.beforeDraw();
this.applyStyle();
this.currentCanvasContext.beginPath();
}
/**
* @inheritDoc
*/
protected draw(context) {
super.draw(context);
if (this.isMask) {
this._context.clip();
this.maskVisible && this.fillAndStoke()
} else {
this.fillAndStoke()
}
}
/**
* 应用渲染样式
*/
protected applyStyle() {
const {currentCanvasContext, fillColor, borderColor, borderWidth} = this;
currentCanvasContext.fillStyle = fillColor;
if (borderWidth > 0) {
currentCanvasContext.strokeStyle = borderColor;
currentCanvasContext.lineWidth = borderWidth;
}
}
/**
* 绘制
*/
protected fillAndStoke() {
const {currentCanvasContext, borderWidth} = this;
currentCanvasContext.fill();
if (borderWidth > 0) {
currentCanvasContext.stroke();
}
}
/**
* @inheritDoc
*/
protected drawClip() {
this.isMask && this.currentCanvasContext.save();
}
/**
* @inheritDoc
*/
onRegression() {
this.isMask && this.currentCanvasContext.restore();
}
onEditorRegression(){
this.onRegression();
}
}
/**
* Created by rockyl on 2019-07-11.
*
* 图片显示类
*/
import {Node,} from "./Node";
import Texture from "../Texture";
import {deepDirtyFieldDetector, dirtyFieldDetector} from "../../tools/decorators";
import {Vector2D} from "../../support";
import Bounds from "../../support/Bounds";
import {Sheet} from "../Sheet";
export class Image extends Node {
/**
* 纹理资源
*/
@dirtyFieldDetector
texture: Texture;
/**
* 填充模式
*/
@dirtyFieldDetector
fillMode: FillMode = FillMode.NORMAL;
/**
* tiled模式的偏移
*/
@deepDirtyFieldDetector
tiledOffset: Vector2D = new Vector2D();
/**
* 切片边界配置
*/
@deepDirtyFieldDetector
slicedBounds: Bounds = new Bounds();
protected _pattern;
protected _sheetForSliced: Sheet = new Sheet();
/**
* 异步资源
* @param promise
*/
private set async_texture(promise: Promise<Texture>) {
if (!promise) {
return;
}
promise.then(
(texture) => {
this.texture = texture;
}
)
}
/**
* 重写缓存模式
*/
protected getUseCacheMode() {
return this._useCacheMode || this.fillMode === FillMode.TILED || this.fillMode === FillMode.SLICED;
}
/**
* 准备切片用的图集
*/
protected prepareSheetForSliced() {
if (this.dirty && !this.slicedBounds.isEmpty) {
const sheet = this._sheetForSliced;
sheet.cleanAllTexture();
const {sourceW, sourceH} = this.texture;
const {x, y, width, height, right, bottom} = this.slicedBounds;
const xs = [0, x, right];
const ys = [0, y, bottom];
const ws = [x, width, sourceW - right];
const hs = [y, height, sourceH - bottom];
const frames = {};
for(let i = 0; i < 9 ; i++){
let gx = i % 3;
let gy = Math.floor(i / 3);
frames[i] = {x: xs[gx], y: ys[gy], width: ws[gx], height: hs[gy],}
}
sheet.init(this.texture, frames);
sheet.generateAll();
}
}
protected draw(context) {
super.draw(context);
this.drawImage(context);
}
/**
* 根据模式绘制图片
*/
drawImage(context) {
if (!this.texture) {
return;
}
const {texture, fillMode, width, height} = this;
const {x, y, sourceW, sourceH} = texture;
let drawWidth = width, drawHeight = height;
switch (fillMode) {
case FillMode.NORMAL:
texture.drawToCanvas(context, 0, 0, undefined, undefined, width, height);
break;
case FillMode.SLICED:
this.prepareSheetForSliced();
const {x: sx, y: sy, right, bottom} = this.slicedBounds;
const rightWidth = sourceW - right;
const bottomHeight = sourceH - bottom;
const xs = [0, sx, drawWidth - rightWidth];
const ys = [0, sy, drawHeight - bottomHeight];
const ws = [sx, drawWidth - sx - rightWidth, rightWidth];
const hs = [sy, drawHeight - sy - bottomHeight, bottomHeight];
for (let i = 0; i < 9; i++) {
const slicedTexture = this._sheetForSliced.getTexture(i);
let gx = i % 3;
let gy = Math.floor(i / 3);
console.log(xs[gx], ys[gy]);
slicedTexture.drawToCanvas(context, xs[gx], ys[gy], undefined, undefined, ws[gx], hs[gy]);
}
break;
case FillMode.TILED:
if (!this._pattern || texture['isDirty']) {
const textureCanvas = texture.getCacheCanvas();
this._pattern = context.createPattern(textureCanvas, 'repeat');
}
if (!this.tiledOffset.isZero) {
const {x: offX, y: offY} = this.tiledOffset;
let x = offX % sourceW;
x = x > 0 ? x - sourceW : x;
let y = offY % sourceH;
y = y > 0 ? y - sourceH : y;
drawWidth -= x;
drawHeight -= y;
context.transform(1, 0, 0, 1, x, y);
}
context.beginPath();
context.rect(0, 0, drawWidth, drawHeight);
context.closePath();
context.fillStyle = this._pattern;
context.fill();
if (!this.tiledOffset.isZero) {
//ctx.restore();
}
break;
}
}
protected measureRenderSize() {
if (this.texture) {
const {sourceW, sourceH} = this.texture;
return {
width: sourceW,
height: sourceH,
}
}
}
}
export enum FillMode {
/**
* 正常
*/
NORMAL,
/**
* 九宫裁切
*/
SLICED,
/**
* 瓦片
*/
TILED,
}
/**
* Created by rockyl on 2019-07-04.
*
* 节点类
*/
import Vector2D from "../../support/Vector2D";
import {deepDirtyFieldDetector, dirtyFieldDetector,} from "../../tools/decorators";
import Matrix from "../../support/Matrix";
import {Bounds} from "../../support";
import {Component, createCanvas, engine, ScillaEngine} from "../index";
import {bubbling, traverse} from "../utils";
import {EngineConfig} from "../../engine-config";
import {EventDispatcher} from "../EventDispatcher";
export class Node extends EventDispatcher {
name = 'Node';
/**
* 坐标
*/
@deepDirtyFieldDetector
position: Vector2D = new Vector2D();
/**
* 缩放
*/
@deepDirtyFieldDetector
scale: Vector2D = new Vector2D(1, 1);
/**
* 旋转
*/
@dirtyFieldDetector
rotation = 0;
/**
* 节点透明度
*/
@dirtyFieldDetector
alpha: number = 1;
/**
* 可见性
*/
@dirtyFieldDetector
visible: boolean = true;
/**
* 锚点
*/
@deepDirtyFieldDetector
anchor: Vector2D = new Vector2D(0, 0);
/**
* 影响子节点
*/
@dirtyFieldDetector
affectChildren: boolean = true;
/**
* 唯一id
*/
protected _uuid: string;
/**
* 组件
*/
protected _components: Component[] = [];
/**
* 父节点
*/
protected _parent: Node = null;
/**
* 子节点
*/
protected _children: Node[] = [];
/**
* 是否游离状态
*/
protected _isFree: boolean = true;
/**
* 是否有效
*/
protected _enabled: boolean = EngineConfig.nodeEnabled;
/**
* 渲染上下文
*/
protected _context;
/**
* 缓存画布
*/
protected _cacheCanvas = null;
/**
* 缓存用的渲染上下文
*/
protected _cacheContext;
/**
* 属性是否脏
*/
protected dirty = true;
/**
* 本地矩阵
*/
protected _localMatrix: Matrix = Matrix.create();
/**
* 全局矩阵
*/
protected _globalMatrix: Matrix = Matrix.create();
/**
* 全局逆矩阵
*/
protected _globalInvertMatrix: Matrix = Matrix.create();
/**
* 真实渲染透明度
*/
protected _renderVisible: boolean;
/**
* 真实渲染透明度
*/
protected _renderAlpha: number;
/**
* 本地坐标缓存
*/
protected _localPos: Vector2D = new Vector2D();
/**
* 全局坐标缓存
*/
protected _globalPosition: Vector2D = new Vector2D();
/**
* 锚点实际偏移
*/
protected _anchorOffset: Vector2D = new Vector2D();
/**
* 渲染边界
*/
protected _bounds = new Bounds();
/**
* 尺寸
* 对于不同的子类渲染都有不同的效果
*/
protected _width: number = NaN;
protected _height: number = NaN;
/**
* 扩展尺寸
*/
protected _margin: number = 0;
/**
* 是否使用渲染模式
*/
protected _useCacheMode = false;
constructor(name?: string, uuid?: string) {
super();
if (name) {
this.name = name;
}
if (uuid) {
this._uuid = uuid;
}
}
/**
* 当属性更新时
* @param value
* @param key
* @param oldValue
*/
onModify(value, key, oldValue) {
this.dirty = true;
}
//----- tree
/**
* 获取父节点
*/
get parent(): Node {
return this._parent;
}
/**
* 获取根节点
*/
get root(): RootNode {
let p: any = this;
do {
if (p instanceof RootNode) {
return p;
}
}
while (p = p.parent)
}
/**
* 是否含有子节点
* @param child
*/
containsChild(child: Node): boolean {
return this.getChildIndex(child) >= 0;
}
/**
* 是否有效状态
*/
get enabled(): boolean {
return this._enabled;
}
set enabled(value: boolean) {
if (this._enabled !== value) {
this.setEnabled(value);
}
}
protected setEnabled(value) {
this._enabled = value;
let that = this;
traverse(this, function (child: Node) {
if (child !== that && !child._enabled) {
return true;
}
child._invokeEnabledState(value);
return false;
}, -1, true)
}
_invokeEnabledState(enabled: boolean) {
if (this._enabled) {
if (enabled) {
this.onEnable();
} else {
this.onDisable();
}
}
}
get isParentActive(): boolean {
return this._parent && this._parent.enabled && !this._parent.isFree;
}
get isActive(): boolean {
return this.isParentActive && this._enabled;
}
/**
* 是否游离状态
*/
get isFree(): boolean {
return this._isFree;
}
/**
* 使游离
*/
_free() {
this._isFree = true;
traverse(this, function (child: Node) {
if (child.isParentActive) {
child._invokeEnabledState(false);
}
child._free();
return false;
}, 1)
}
/**
* 使约束
*/
_restrict() {
this._isFree = false;
traverse(this, function (child: Node) {
if (child.isParentActive) {
child._invokeEnabledState(true);
}
child._restrict();
return false;
}, 1)
}
/**
* 添加子节点时
* @param child
* @private
*/
protected _onChildAdded(child: Node) {
child._parent = this;
if (!this._isFree && child._isFree) {
if (child.isParentActive) {
child._invokeEnabledState(true);
}
child._restrict();
}
}
/**
* 子节点被移除时
* @param child
* @private
*/
_onChildRemoved(child: Node) {
child._parent = null;
if (!this._isFree && !child._isFree) {
if (child.isActive) {
child._invokeEnabledState(false);
}
child._free();
}
}
/**
* 添加子节点,重复添加则会加到最后
* @param child
*/
addChild(child: Node) {
this.addChildAt(child, this._children.length);
}
/**
* 添加子节点到指定索引,重复添加可作为顺序交换
* @param child
* @param index
*/
addChildAt(child: Node, index) {
if (child.parent && child.parent !== this) {
child.parent.removeChild(child);
}
const currentIndex = this.getChildIndex(child);
if (index < 0 || currentIndex == index) { //如果索引小于0或没变化
return;
}
index = Math.min(this._children.length, index);
if (currentIndex >= 0 || index < this._children.length) {
if (currentIndex >= 0) {
this._children.splice(currentIndex, 1);
}
this._children.splice(index, 0, child);
} else {
this._children.push(child);
}
this._onChildAdded(child);
}
/**
* 移除节点
* @param child
*/
removeChild(child: Node) {
const index = this.getChildIndex(child);
if (index >= 0) {
return this.removeChildAt(index);
}
}
/**
* 移除指定索引的节点
* @param index
*/
removeChildAt(index: number) {
const child = this._children[index];
if (child) {
this._onChildRemoved(child);
this._children.splice(index, 1);
return child;
}
}
/**
* 根据节点获取索引
* @param child
*/
getChildIndex(child: Node): number {
return this._children.indexOf(child)
}
/**
* 获取指定索引的节点
* @param index
*/
getChildAt(index): Node {
return this._children[index];
}
/**
* 获取指定名字的节点
* @param name
*/
getChildrenByName(name): Node[] {
let children = [];
for (let child of this._children) {
if (child.name === name) {
children.push(child);
}
}
return children;
}
/**
* 移除所有子节点
*/
removeChildren() {
while (this._children.length > 0) {
this.removeChildAt(0);
}
}
/**
* 获取所有子节点
*/
get children(): Node[] {
return this._children;
}
/**
* 获取唯一ID
*/
get uuid(): string {
return this._uuid;
}
/**
* 移除所有组件和子节点
*/
clean() {
this.removeAllComponents();
this.removeChildren();
}
//----- component
/**
* 增加组件
* @param component
*/
addComponent(component: Component) {
this.onAddComponent(component);
this._components.push(component);
}
/**
* 在指定索引增加组件,重复添加可作为顺序交换
* @param component
* @param index
*/
addComponentAt(component: Component, index: number) {
const currentIndex = this._components.indexOf(component);
if (currentIndex == index) {
return;
}
if (currentIndex >= 0) {
this._components.splice(currentIndex, 1);
}
this._components.splice(index, 0, component);
this.onAddComponent(component);
}
/**
* 移除组件
* @param component
*/
removeComponent(component: Component) {
this.onRemoveComponent(component);
const index = this._components.indexOf(component);
if (index >= 0) {
this._components.splice(index, 1);
}
}
/**
* 移除所有组件
*/
removeAllComponents() {
while (this._components.length > 0) {
this.removeComponent(this._components[0]);
}
}
/**
* 根据组件名称获取指定类的组件列表
* @param name
*/
getComponentsByName<T extends Component>(name: string): T[] {
return <T[]>this._components.filter((value: Component) => {
return value.constructor['__class__'] === name;
});
}
/**
* 获取指定类的组件列表
* @param clazz
*/
getComponents<T extends Component>(clazz: new() => T): T[] {
return <T[]>this._components.filter((component: Component) => {
return component instanceof clazz;
});
}
/**
* 获取指定类的组件
* @param name
*/
getComponentByName<T extends Component>(name: string): T {
return this.getComponentsByName<T>(name)[0];
}
/**
* 获取指定类的组件
* @param clazz
*/
getComponent<T extends Component>(clazz: new() => T): T {
return this.getComponents<T>(clazz)[0];
}
/**
* 获取所有组件
*/
get components(): Component[] {
return this._components;
}
//----- lifecycle
/**
* 遍历所有组件
* @param func
*/
forEachComponent(func: (component) => boolean) {
for (let component of this._components) {
if (func(component)) {
break;
}
}
}
active() {
this.forEachComponent(comp => {
if (comp.enabled) {
return comp.onCreate();
}
});
}
/**
* 当组件生效时
*/
onEnable() {
this.forEachComponent(comp => {
if (comp.enabled) {
return comp.$onAwake();
}
});
}
/**
* 当组件失效时
*/
onDisable() {
this.forEachComponent(comp => {
if (comp.enabled) {
return comp.$onSleep();
}
});
}
/**
* 当时钟更新时
* @param t
*/
$onUpdate(t) {
if (!this.enabled) {
return;
}
this.preUpdate(t);
this.updateComponents(t);
this.onUpdate(t);
this.windUpUpdate(t);
}
/**
* 当子节点遍历回归
*/
onRegression() {
this.forEachComponent(comp => {
if (comp.enabled) {
if (EngineConfig.editorMode) {
return comp.$onEditorRegression();
} else {
return comp.$onRegression();
}
}
});
}
/**
* 当交互时
* @param type
* @param event
*/
onInteract(type, event) {
if (!this.isFree && this.enabled) {
let interrupt = false;
this.forEachComponent(comp => {
if (comp.enabled && comp.interactable) {
const r = comp.onInteract(type, event);
if (r) {
interrupt = true;
}
return false;
}
});
return interrupt;
} else {
return false;
}
}
/**
* 当添加组件时
* @param component
*/
onAddComponent(component: Component) {
component._setup(this);
if (EngineConfig.awakeComponentWhenAdded) {
this.awakeComponent(component);
}
}
/**
* 唤醒组件
* @param component
*/
awakeComponent(component) {
if (!this._isFree && this._enabled) {
component.$onAwake();
}
}
/**
* 当移除组件时
* @param component
*/
onRemoveComponent(component: Component) {
if (EngineConfig.sleepComponentWhenRemoved) {
this.sleepComponent(component);
}
component._unSetup();
}
/**
* 睡眠组件
* @param component
*/
sleepComponent(component: Component) {
if (!this._isFree && this._enabled) {
component.$onSleep();
}
}
/**
* 向下广播
* 如果某组件调用后返回true,将结束整条链
* @param method 方法名
* @param level 深度,默认全部遍历
* @param params 参数
*/
broadcast(method: string, level = -1, ...params) {
traverse(this, this.invokeOnNode, level, true, null, method, ...params)
}
/**
* 向上冒泡
* 如果某组件调用后返回true,将结束整条链
* @param method 方法名
* @param params 参数
*/
bubbling(method: string, ...params) {
bubbling(this, this.invokeOnNode, false, method, ...params);
}
/**
* 调用节点上的组件的方法
* @param hitNode 遇到的节点
* @param method 方法名
* @param params 参数
*/
private invokeOnNode = (hitNode: Node, method: string, ...params): boolean => {
let hitBreak = false;
hitNode.forEachComponent(comp => {
const m = comp[method];
if (m) {
const result = m.apply(comp, params);
if (result) {
hitBreak = true;
}
}
return false;
});
if (hitBreak) {
return true;
}
};
//-----Update
/**
* 预更新
* @param t
*/
protected preUpdate(t) {
}
/**
* 组件更新
* @param t
*/
protected updateComponents(t) {
this.forEachComponent(comp => {
if (comp.enabled) {
if (EngineConfig.editorMode) {
return comp.$onEditorUpdate(t);
} else {
return comp.$onUpdate(t);
}
}
});
}
/**
* 更新
* @param t
*/
protected onUpdate(t) {
}
/**
* 更新收尾
* @param t
*/
protected windUpUpdate(t) {
if (this.dirty) {
this.updateLocalMatrix();
}
this.updateGlobalMatrix();
if (this.dirty) {
if (this.useCacheMode) {
this.readyCacheCanvas();
}
this.measureBounds();
if (this.useCacheMode) {
this.updateCacheCanvas();
}
}
this.transformToLocal();
if (this._renderVisible) {
this.render();
}
this.dirty = false;
}
/**
* 准备缓存渲染上下文
*/
protected readyCacheCanvas() {
let canvas = this._cacheCanvas;
if (!canvas) {
canvas = this._cacheCanvas = createCanvas();
this._cacheContext = canvas.getContext('2d');
}
}
/**
* 更新缓存属性
*/
protected updateCacheCanvas() {
let canvas = this._cacheCanvas;
const {width, height} = this.bounds;
canvas.width = width + this._margin * 2;
canvas.height = height + this._margin * 2;
}
/**
* 测量边界
*/
measureBounds() {
if (!this.dirty) {
return;
}
const {bounds, explicitWidth, explicitHeight, anchor: {x, y}} = this;
let renderSize: any = this.measureRenderSize();
bounds.width = isNaN(explicitWidth) ? renderSize ? renderSize.width : 0 : explicitWidth;
bounds.height = isNaN(explicitHeight) ? renderSize ? renderSize.height : 0 : explicitHeight;
const anchorOffsetX = this._anchorOffset.x = bounds.width * x;
const anchorOffsetY = this._anchorOffset.y = bounds.height * y;
bounds.x = -anchorOffsetX;
bounds.y = -anchorOffsetY;
}
/**
* 测量渲染尺寸
*/
protected measureRenderSize() {
return;
}
/**
* 渲染过程
*/
protected render() {
this.applyAlpha();
this.beforeDraw();
this.drawClip();
if (this.dirty) {
if (this.useCacheMode) {
this.draw(this.currentCanvasContext);
}
}
if (this.useCacheMode) {
this.drawCache();
} else {
this.draw(this.currentCanvasContext);
}
}
/**
* 应用透明度
*/
protected applyAlpha() {
this.context.globalAlpha = this.alpha * this._renderAlpha;
}
/**
* 画之前
*/
protected beforeDraw() {
}
/**
* 画遮罩
*/
protected drawClip() {
}
/**
* 画
*/
protected draw(context) {
}
/**
* 绘制缓存
*/
protected drawCache() {
if (this._cacheCanvas.width > 0 && this._cacheCanvas.height > 0) {
this.context.drawImage(this._cacheCanvas, -this._margin, -this._margin);
}
}
private get context() {
if (!this._context) {
this._context = engine.renderContext.context;
}
return this._context;
}
protected getUseCacheMode() {
return this._useCacheMode;
}
/**
* 获取全局坐标
*/
get globalPosition() {
this._globalPosition.setXY(this._globalMatrix.tx, this._globalMatrix.ty);
return this._globalPosition;
}
/**
* 宽度
*/
get width(): number {
return isNaN(this._width) ? this.bounds.width : this._width;
}
set width(value: number) {
if (this._width != value) {
let old = this._width;
this._width = value;
this.onModify(value, 'width', old);
}
}
/**
* 设定的宽度
*/
get explicitWidth(): number {
return this._width;
}
/**
* 高度
*/
get height(): number {
return isNaN(this._height) ? this.bounds.height : this._height;
}
set height(value: number) {
if (this._height != value) {
let old = this._height;
this._height = value;
this.onModify(value, 'height', old);
}
}
/**
* 是否使用渲染模式
*/
get useCacheMode() {
return this.getUseCacheMode();
}
set useCacheMode(value) {
this._useCacheMode = value;
}
/**
* 设定的高度
*/
get explicitHeight(): number {
return this._height;
}
/**
* 全局坐标转本地坐标
* @param position
*/
globalPositionToLocal(position?) {
const matrix = this.getMatrix(true, true, true);
matrix.transformPoint(position ? position.x : 0, position ? position.y : 0, this._localPos);
return this._localPos;
}
/**
* 获取全局角度
*/
get globalRotation() {
return this._globalMatrix.rotation * 180 / Math.PI;
}
/**
* 实际渲染透明度
*/
get renderAlpha(): number {
return this._renderAlpha;
}
/**
* 实际可见性
*/
get renderVisible(): boolean {
return this._renderVisible;
}
/**
* 渲染边界
*/
get bounds(): Bounds {
return this._bounds;
}
/**
* 获取矩阵
*/
getMatrix(invert: boolean = false, affectChildren = false, invertOnlyTranslate = false): Matrix {
let matrix;
if (this.affectChildren || affectChildren) {
matrix = this._globalMatrix;
if (invert) {
const invertMatrix = this._globalInvertMatrix;
invertMatrix.copyFrom(matrix);
if (invertOnlyTranslate) {
invertMatrix.a = 1;
invertMatrix.d = 1;
}
invertMatrix.invert();
return invertMatrix;
}
} else {
matrix = this.parent.getMatrix(invert);
}
return matrix;
}
/**
* 更新本地矩阵
*/
protected updateLocalMatrix() {
const {
position: {x, y},
scale: {x: sx, y: sy}, rotation,
anchor: {x: ax, y: ay},
width, height,
} = this;
const matrix = this._localMatrix;
matrix.identity();
/*matrix.translate(
-ax * width,
-ay * height,
);*/
matrix.scale(sx, sy);
matrix.rotate(rotation * Math.PI / 180);
matrix.translate(
x,
y,
);
}
/**
* 更新全局矩阵
*/
protected updateGlobalMatrix() {
const {
_globalMatrix, _localMatrix,
parent,
} = this;
_globalMatrix.copyFrom(_localMatrix);
if (parent) {
this._renderAlpha = parent.renderAlpha * this.alpha;
this._renderVisible = parent.renderVisible && this.visible;
_globalMatrix.concat(parent.getMatrix(false));
} else {
this._renderAlpha = this.alpha;
this._renderVisible = this.visible;
}
}
/**
* 执行矩阵转换
*/
protected transformToLocal() {
const {_anchorOffset: {x: ax, y: ay}} = this;
const {a, b, c, d, tx, ty} = this.getMatrix(false, true);
const offX = ax * a + ay * c;
const offY = ax * b + ay * d;
this.context.setTransform(a, b, c, d, tx - offX, ty - offY);
}
/**
* 获取渲染上下文
* 如果缓存上下文存在,则返回缓存上下文
*/
protected get currentCanvasContext() {
return this._cacheContext || this.context;
}
}
/**
* 根节点类
*/
export class RootNode extends Node {
protected _engine: ScillaEngine;
constructor(engine: ScillaEngine) {
super();
this.name = 'root';
this._engine = engine;
}
/**
* 获取引擎,只有活动的root节点才有这个属性
*/
get engine() {
return this._engine;
}
}
/**
* Created by rockyl on 2019-07-05.
*
* (圆角)矩形
*/
import {GraphicNode} from "./GraphicNode";
import {dirtyFieldDetector} from "../../tools/decorators";
export class Rect extends GraphicNode {
/**
* 圆角
*/
@dirtyFieldDetector
cornerRadius = 0;
/**
* @inheritDoc
*/
protected draw(context) {
const {PI} = Math;
const {cornerRadius: r, bounds: {width, height}, _margin, _useCacheMode} = this;
let offset = _useCacheMode ? _margin : 0;
if (r) {
context.moveTo(offset + r, offset);
context.lineTo(offset + width - r, offset);
context.arc(offset + width - r, offset + r, r, PI * 3 / 2, PI * 2);
context.lineTo(offset + width, offset + height - r);
context.arc(offset + width - r, offset + height - r, r, 0, PI / 2);
context.lineTo(offset + r, offset + height);
context.arc(offset + r, offset + height - r, r, PI / 2, PI);
context.lineTo(offset, offset + r);
context.arc(offset + r, offset + r, r, PI, PI * 3 / 2);
} else {
context.rect(offset, offset, width, height);
}
super.draw(context);
}
}
/**
* Created by rockyl on 2019-07-05.
*/
export * from './Node'
export * from './GraphicNode'
export * from './Rect'
export * from './Circle'
export * from './Image'
/** /**
* Created by rockyl on 2019-04-22. * Created by rockyl on 2019-04-22.
* *
* 实体相关工具 * 节点相关工具
*/ */
import {Entity} from "./Entity"; import {Node} from "./nodes/Node";
import {Component} from "./Component";
/** /**
* 实体遍历(先序遍历) * 节点遍历(先序遍历)
* @param target 目标实体 * @param target 目标节点
* @param hitChild 遇到子实体回调 * @param hitChild 遇到子节点回调
* @param level 深度,默认全部遍历 * @param level 深度,默认全部遍历
* @param includeSelf 是否包括自身 * @param includeSelf 是否包括自身
* @param fullCallback 子实体遍历完后回调 * @param fullCallback 子节点遍历完后回调
* @param params 其他参数 * @param params 其他参数
*/ */
export function traverse(target: Entity, hitChild: (child: Entity, ...params) => boolean, level = -1, includeSelf = false, fullCallback?: (current: Entity) => void, ...params) { export function traverse(target: Node, hitChild: (child: Node, ...params) => boolean, level = -1, includeSelf = false, fullCallback?: (current: Node) => void, ...params) {
let interrupt; let interrupt;
if (includeSelf) { if (includeSelf) {
hitChild(target, ...params); hitChild(target, ...params);
...@@ -34,21 +33,19 @@ export function traverse(target: Entity, hitChild: (child: Entity, ...params) => ...@@ -34,21 +33,19 @@ export function traverse(target: Entity, hitChild: (child: Entity, ...params) =>
} }
} }
fullCallback && fullCallback(target); !interrupt && fullCallback && fullCallback(target);
} }
/** /**
* 实体遍历(后序遍历且倒序) * 节点遍历(后序遍历且倒序)
* @param target 目标实体 * @param target 目标节点
* @param hitChild 遇到子实体回调 * @param hitChild 遇到子节点回调
* @param level 深度,默认全部遍历 * @param level 深度,默认全部遍历
* @param includeSelf 是否包括自身 * @param includeSelf 是否包括自身
* @param fullCallback 子实体遍历完后回调 * @param fullCallback 子节点遍历完后回调
* @param params 其他参数 * @param params 其他参数
*/ */
export function traversePostorder(target: Entity, hitChild: (child: Entity, ...params) => boolean, level = -1, includeSelf = false, fullCallback?: (current: Entity) => void, ...params) { export function traversePostorder(target: Node, hitChild: (child: Node, ...params) => boolean, level = -1, includeSelf = false, fullCallback?: (current: Node) => void, ...params) {
let interrupt;
if (level !== 0) { if (level !== 0) {
for (let i = target.children.length - 1; i >= 0; i--) { for (let i = target.children.length - 1; i >= 0; i--) {
const child = target.children[i]; const child = target.children[i];
...@@ -69,23 +66,23 @@ export function traversePostorder(target: Entity, hitChild: (child: Entity, ...p ...@@ -69,23 +66,23 @@ export function traversePostorder(target: Entity, hitChild: (child: Entity, ...p
hitChild(target, ...params); hitChild(target, ...params);
} }
!interrupt && fullCallback && fullCallback(target); fullCallback && fullCallback(target);
} }
/** /**
* 实体冒泡 * 节点冒泡
* @param target 目标实体 * @param target 目标节点
* @param hitParent 遇到父实体回调 * @param hitParent 遇到父节点回调
* @param includeSelf 是否包括自身 * @param includeSelf 是否包括自身
* @param params 其他参数 * @param params 其他参数
*/ */
export function bubbling(target: Entity, hitParent: (parent: Entity, ...params) => boolean, includeSelf = false, ...params) { export function bubbling(target: Node, hitParent: (parent: Node, ...params) => boolean, includeSelf = false, ...params) {
if (includeSelf) { if (includeSelf) {
hitParent(target, ...params); hitParent(target, ...params);
} }
let entity = target; let node = target;
while (entity = entity.parent) { while (node = node.parent) {
if (hitParent(entity, ...params)) { if (hitParent(node, ...params)) {
break; break;
} }
} }
......
...@@ -15,19 +15,19 @@ export const EngineConfig = { ...@@ -15,19 +15,19 @@ export const EngineConfig = {
*/ */
lineHeightRatio: 1.2, lineHeightRatio: 1.2,
/** /**
* 实体实例化时enabled的初始值 * 节点实例化时enabled的初始值
*/ */
entityEnabled: true, nodeEnabled: true,
/** /**
* 组件实例化时enabled的初始值 * 组件实例化时enabled的初始值
*/ */
componentEnabled: true, componentEnabled: true,
/** /**
* 组件被添加到实体树上时是否执行onAwake方法 * 组件被添加到节点树上时是否执行onAwake方法
*/ */
awakeComponentWhenAdded: true, awakeComponentWhenAdded: true,
/** /**
* 组件从实体树上被移除时是否执行onSleep方法 * 组件从节点树上被移除时是否执行onSleep方法
*/ */
sleepComponentWhenRemoved: true, sleepComponentWhenRemoved: true,
/** /**
......
...@@ -18,7 +18,7 @@ export default class Bounds { ...@@ -18,7 +18,7 @@ export default class Bounds {
@dirtyFieldTrigger @dirtyFieldTrigger
height: number; height: number;
_onChange: Function; onChange: Function;
constructor(x: number = 0, y: number = 0, width: number = 0, height: number = 0, onChange?) { constructor(x: number = 0, y: number = 0, width: number = 0, height: number = 0, onChange?) {
this.x = x; this.x = x;
...@@ -26,11 +26,11 @@ export default class Bounds { ...@@ -26,11 +26,11 @@ export default class Bounds {
this.width = width; this.width = width;
this.height = height; this.height = height;
this._onChange = onChange; this.onChange = onChange;
} }
onModify(value, key, oldValue){ onModify(value, key, oldValue){
this._onChange && this._onChange(value, key, oldValue); this.onChange && this.onChange(value, key, oldValue);
} }
get left(): number { get left(): number {
......
...@@ -68,11 +68,11 @@ export default class DataCenter extends EventEmitter { ...@@ -68,11 +68,11 @@ export default class DataCenter extends EventEmitter {
* @param expression * @param expression
*/ */
public parse(type: string, expression: string) { public parse(type: string, expression: string) {
let entity = this.store[type]; let node = this.store[type];
let result = null; let result = null;
try { try {
result = utils.dotEval(expression, entity); result = utils.dotEval(expression, node);
} catch (e) { } catch (e) {
} }
......
...@@ -8,7 +8,7 @@ import HashObject from "../core/HashObject"; ...@@ -8,7 +8,7 @@ import HashObject from "../core/HashObject";
/** /**
* 尺寸类 * 尺寸类
*/ */
export default class Size extends HashObject{ export default class Size extends HashObject {
/** /**
* 宽度 * 宽度
*/ */
...@@ -32,18 +32,26 @@ export default class Size extends HashObject{ ...@@ -32,18 +32,26 @@ export default class Size extends HashObject{
this.height = height; this.height = height;
} }
onModify(value, key, oldValue) {
this.onChange && this.onChange(value, key, oldValue);
}
/** /**
* 置空 * 置空
*/ */
setNaN(){ setNaN() {
this.width = NaN; this.width = NaN;
this.height = NaN; this.height = NaN;
} }
isNaN() {
return isNaN(this.width) && isNaN(this.height);
}
/** /**
* 是否空尺寸 * 是否空尺寸
*/ */
get isEmpty(){ get isEmpty() {
return this.width === 0 && this.height === 0; return this.width === 0 && this.height === 0;
} }
...@@ -77,8 +85,4 @@ export default class Size extends HashObject{ ...@@ -77,8 +85,4 @@ export default class Size extends HashObject{
this.width = target.width; this.width = target.width;
this.height = target.height; this.height = target.height;
} }
onModify(value, key, oldValue) {
this.onChange && this.onChange(value, key, oldValue);
}
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import {get, recycle, register} from "./ObjectPool"; import {get, recycle, register} from "./ObjectPool";
import HashObject from "../core/HashObject"; import HashObject from "../core/HashObject";
import {dirtyFieldTrigger} from "../tools/decorators";
const name = 'Vector2D'; const name = 'Vector2D';
register(name, function () { register(name, function () {
...@@ -34,9 +35,18 @@ export function releaseVector2D(target) { ...@@ -34,9 +35,18 @@ export function releaseVector2D(target) {
* 2D矢量 * 2D矢量
*/ */
export default class Vector2D extends HashObject { export default class Vector2D extends HashObject {
private _x: number; /**
private _y: number; * x分量
private onChange: Function; */
@dirtyFieldTrigger
x: number;
/**
* y分量
*/
@dirtyFieldTrigger
y: number;
onChange: Function;
/** /**
* 创建一个2D矢量 * 创建一个2D矢量
...@@ -49,41 +59,11 @@ export default class Vector2D extends HashObject { ...@@ -49,41 +59,11 @@ export default class Vector2D extends HashObject {
this.onChange = onChange; this.onChange = onChange;
this._x = 0;
this._y = 0;
this.setXY(x, y); this.setXY(x, y);
} }
/** onModify(value, key, oldValue) {
* x分量 this.onChange && this.onChange(value, key, oldValue);
*/
get x(): number {
return this._x;
}
set x(v: number) {
if (this._x !== v) {
const old = this._x;
this._x = v;
this.onChange && this.onChange(v, 'x', old);
}
}
/**
* y分量
*/
get y(): number {
return this._y;
}
set y(v: number) {
if (this._y !== v) {
const old = this._y;
this._y = v;
this.onChange && this.onChange(v, 'y', old);
}
} }
/** /**
......
...@@ -6,9 +6,9 @@ ...@@ -6,9 +6,9 @@
/** /**
* 属性修改时触发 * 属性修改时触发
* @param onChange * @param onModify
*/ */
export function fieldChanged(onChange) { export function fieldChanged(onModify) {
return function (target: any, key: string) { return function (target: any, key: string) {
const privateKey = '_' + key; const privateKey = '_' + key;
Object.defineProperty(target, key, { Object.defineProperty(target, key, {
...@@ -20,7 +20,7 @@ export function fieldChanged(onChange) { ...@@ -20,7 +20,7 @@ export function fieldChanged(onChange) {
const oldValue = this[privateKey]; const oldValue = this[privateKey];
if (oldValue !== v) { if (oldValue !== v) {
this[privateKey] = v; this[privateKey] = v;
onChange.apply(this, [v, key, oldValue]); onModify.apply(this, [v, key, oldValue]);
} }
} }
}) })
...@@ -36,6 +36,21 @@ export const dirtyFieldDetector = fieldChanged( ...@@ -36,6 +36,21 @@ export const dirtyFieldDetector = fieldChanged(
} }
); );
/**
* 深度属性变脏时设置宿主的dirty属性为true
*/
export const deepDirtyFieldDetector = fieldChanged(
function (value, key, oldValue) {
const scope = this;
scope['dirty'] = true;
if (typeof value === 'object') {
value['onModify'] = function(){
scope['dirty'] = true;
};
}
}
);
/** /**
* 属性变脏时触发onModify方法 * 属性变脏时触发onModify方法
*/ */
...@@ -44,3 +59,18 @@ export const dirtyFieldTrigger = fieldChanged( ...@@ -44,3 +59,18 @@ export const dirtyFieldTrigger = fieldChanged(
this['onModify'] && this['onModify'](value, key, oldValue); this['onModify'] && this['onModify'](value, key, oldValue);
} }
); );
/**
* 深入属性变脏时触发onModify方法
*/
export const deepDirtyFieldTrigger = fieldChanged(
function (value: any, key, oldValue) {
if (this['onModify']) {
this['onModify'](value, key, oldValue);
if (typeof value === 'object') {
value['onModify'] = this['onModify'];
}
}
}
);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment