olympus-r-dom
Version:
Olympus的DOM表现层库
592 lines (591 loc) • 19.8 kB
JavaScript
/// <amd-module name="DOMBridge"/>
/// <reference types="tween.js"/>
import * as TWEEN from "@tweenjs/tween.js";
import { assetsManager } from "olympus-r/engine/assets/AssetsManager";
import { system } from "olympus-r/engine/system/System";
import { extendObject, getObjectHashs } from "olympus-r/utils/ObjectUtil";
import MaskEntity from "./dom/mask/MaskEntity";
import BackPanelPolicy from "./dom/panel/BackPanelPolicy";
import FadeScenePolicy from "./dom/scene/FadeScenePolicy";
import { copyRef, isDOMPath, isDOMStr, toHTMLElement, wrapSkin } from "./dom/utils/SkinUtil";
/**
* @author Raykid
* @email initial_r@qq.com
* @create date 2017-09-18
* @modify date 2017-09-18
*
* 基于DOM的表现层桥实现
*/
var DOMBridge = /** @class */ (function () {
function DOMBridge(params) {
/**
* 获取默认弹窗策略
*
* @type {IPanelPolicy}
* @memberof DOMBridge
*/
this.defaultPanelPolicy = new BackPanelPolicy();
/**
* 获取默认场景切换策略
*
* @type {IScenePolicy}
* @memberof DOMBridge
*/
this.defaultScenePolicy = new FadeScenePolicy();
this._listenerDict = {};
this._initParams = params;
}
Object.defineProperty(DOMBridge.prototype, "type", {
/**
* 获取表现层类型名称
*
* @readonly
* @type {string}
* @memberof DOMBridge
*/
get: function () {
return DOMBridge.TYPE;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "htmlWrapper", {
/**
* 获取表现层HTML包装器,可以对其样式进行自定义调整
*
* @readonly
* @type {HTMLElement}
* @memberof DOMBridge
*/
get: function () {
return this._initParams.container;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "root", {
/**
* 获取根显示节点
*
* @readonly
* @type {HTMLElement}
* @memberof DOMBridge
*/
get: function () {
return this._initParams.container;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "stage", {
/**
* 获取舞台引用,DOM的舞台指向根节点
*
* @readonly
* @type {HTMLElement}
* @memberof DOMBridge
*/
get: function () {
return this._initParams.container;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "bgLayer", {
/**
* 获取背景容器
*
* @readonly
* @type {HTMLElement}
* @memberof DOMBridge
*/
get: function () {
return this._bgLayer;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "sceneLayer", {
/**
* 获取场景容器
*
* @readonly
* @type {HTMLElement}
* @memberof DOMBridge
*/
get: function () {
return this._sceneLayer;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "frameLayer", {
/**
* 获取框架容器
*
* @readonly
* @type {HTMLElement}
* @memberof DOMBridge
*/
get: function () {
return this._frameLayer;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "panelLayer", {
/**
* 获取弹窗容器
*
* @readonly
* @type {HTMLElement}
* @memberof DOMBridge
*/
get: function () {
return this._panelLayer;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "maskLayer", {
/**
* 获取遮罩容器
*
* @readonly
* @type {HTMLElement}
* @memberof DOMBridge
*/
get: function () {
return this._maskLayer;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "topLayer", {
/**
* 获取顶级容器
*
* @readonly
* @type {HTMLElement}
* @memberof DOMBridge
*/
get: function () {
return this._topLayer;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "promptClass", {
/**
* 获取通用提示框
*
* @readonly
* @type {IPromptPanelConstructor}
* @memberof DOMBridge
*/
get: function () {
return this._initParams.promptClass;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DOMBridge.prototype, "maskEntity", {
/**
* 获取遮罩实体
*
* @readonly
* @type {IMaskEntity}
* @memberof DOMBridge
*/
get: function () {
return new MaskEntity(this._initParams.maskData);
},
enumerable: true,
configurable: true
});
DOMBridge.prototype.createLayer = function (fixed) {
// 生成一个父容器,不响应点击事件,但会撑起全屏幕范围
var layer = document.createElement("div");
layer.style.position = fixed ? "fixed" : "absolute";
layer.style.top = "0%";
layer.style.left = "0%";
layer.style.width = "100%";
layer.style.height = "100%";
layer.style.pointerEvents = "none";
this.root.appendChild(layer);
// 生成一个子容器,实际用来放置子对象,目的是响应点击事件
var subLayer = document.createElement("div");
subLayer.style.pointerEvents = "auto";
layer.appendChild(subLayer);
// 返回子容器
return subLayer;
};
/**
* 初始化表现层桥,可以没有该方法,没有该方法则表示该表现层无需初始化
* @param {()=>void} complete 初始化完毕后的回调
* @memberof DOMBridge
*/
DOMBridge.prototype.init = function (complete) {
// 如果是名称,则转变成引用
if (typeof this._initParams.container == "string") {
this._initParams.container = document.querySelector(this._initParams.container);
}
// 如果是空,则生成一个
if (!this._initParams.container) {
this._initParams.container = document.createElement("div");
document.body.appendChild(this._initParams.container);
}
// 创建背景显示层
this._bgLayer = this.createLayer(false);
// 创建场景显示层
this._sceneLayer = this.createLayer(false);
// 创建框架显示层
this._frameLayer = this.createLayer(true);
// 创建弹出层
this._panelLayer = this.createLayer(true);
// 创建遮罩层
this._maskLayer = this.createLayer(true);
// 创建顶级显示层
this._topLayer = this.createLayer(true);
// 添加Tween.js驱动
system.enterFrame(function () {
// 每次使用最新的当前运行毫秒数更新Tween.js
TWEEN.update(system.getTimer());
});
// 调用回调
complete(this);
};
/**
* 判断皮肤是否是DOM显示节点
*
* @param {HTMLElement|string|string[]} skin 皮肤对象
* @returns {boolean} 是否是DOM显示节点
* @memberof DOMBridge
*/
DOMBridge.prototype.isMySkin = function (skin) {
if (skin instanceof HTMLElement)
return true;
if (typeof skin === "string" && (isDOMPath(skin) || isDOMStr(skin)))
return true;
if (skin instanceof Array) {
// 数组里每一个元素都必须是皮肤
var result = true;
for (var _i = 0, skin_1 = skin; _i < skin_1.length; _i++) {
var temp = skin_1[_i];
if (!(typeof temp === "string" && (isDOMPath(temp) || isDOMStr(temp)))) {
result = false;
break;
}
}
return result;
}
return false;
};
/**
* 包装Element节点
*
* @param {IMediator<Element>} mediator 中介者
* @param {Element|string|string[]} skin 原始Element节点
* @returns {HTMLElement} 包装后的Element节点
* @memberof DOMBridge
*/
DOMBridge.prototype.wrapSkin = function (mediator, skin) {
return wrapSkin(mediator, skin);
};
/**
* 替换皮肤,用于组件变身时不同表现层桥的处理
*
* @param {IMediator<Element>} mediator 中介者
* @param {*} current 当前皮肤
* @param {Element|string|string[]} target 要替换的皮肤
* @returns {*} 替换完毕的皮肤
* @memberof DOMBridge
*/
DOMBridge.prototype.replaceSkin = function (mediator, current, target) {
target = toHTMLElement(target);
// 如果有父节点,则用目标节点替换当前节点位置
var parent = current.parentElement;
if (parent) {
parent.insertBefore(target, current);
parent.removeChild(current);
}
// 重新包装节点
this.wrapSkin(mediator, target);
// 返回皮肤
return target;
};
/**
* 同步皮肤,用于组件变身后的重新定位
*
* @param {HTMLElement} current 当前皮肤
* @param {HTMLElement} target 替换的皮肤
* @memberof DOMBridge
*/
DOMBridge.prototype.syncSkin = function (current, target) {
if (!current || !target)
return;
// DOM无需特意同步,因为其样式都可以以css样式方式在外部表示,而仅有当前节点的style属性是需要同步的
extendObject(target.style, current.style);
};
/**
* 创建一个空的显示对象
*
* @returns {HTMLElement}
* @memberof DOMBridge
*/
DOMBridge.prototype.createEmptyDisplay = function () {
var empty = document.createElement("div");
return empty;
};
/**
* 创建一个占位符
*
* @returns {HTMLElement}
* @memberof DOMBridge
*/
DOMBridge.prototype.createPlaceHolder = function () {
var empty = this.createEmptyDisplay();
empty.style.display = "none";
return empty;
};
/**
* 添加显示
*
* @param {Element} parent 要添加到的父容器
* @param {Element} target 被添加的显示对象
* @return {Element} 返回被添加的显示对象
* @memberof DOMBridge
*/
DOMBridge.prototype.addChild = function (parent, target) {
return parent.appendChild(target);
};
/**
* 按索引添加显示
*
* @param {Element} parent 要添加到的父容器
* @param {Element} target 被添加的显示对象
* @param {number} index 要添加到的父级索引
* @return {Element} 返回被添加的显示对象
* @memberof DOMBridge
*/
DOMBridge.prototype.addChildAt = function (parent, target, index) {
return parent.insertBefore(target, this.getChildAt(parent, index));
};
/**
* 移除显示对象
*
* @param {Element} parent 父容器
* @param {Element} target 被移除的显示对象
* @return {Element} 返回被移除的显示对象
* @memberof DOMBridge
*/
DOMBridge.prototype.removeChild = function (parent, target) {
if (parent && target && target.parentElement === parent)
return parent.removeChild(target);
else
return target;
};
/**
* 按索引移除显示
*
* @param {Element} parent 父容器
* @param {number} index 索引
* @return {Element} 返回被移除的显示对象
* @memberof DOMBridge
*/
DOMBridge.prototype.removeChildAt = function (parent, index) {
return this.removeChild(parent, this.getChildAt(parent, index));
};
/**
* 移除所有显示对象
*
* @param {Element} parent 父容器
* @memberof DOMBridge
*/
DOMBridge.prototype.removeChildren = function (parent) {
for (var i = 0, len = parent.children.length; i < len; i++) {
parent.removeChild(parent.children.item(0));
}
};
/**
* 获取父容器
*
* @param {Element} target 目标对象
* @returns {Element} 父容器
* @memberof DOMBridge
*/
DOMBridge.prototype.getParent = function (target) {
return target.parentElement;
};
/**
* 获取指定索引处的显示对象
*
* @param {Element} parent 父容器
* @param {number} index 指定父级索引
* @return {Element} 索引处的显示对象
* @memberof DOMBridge
*/
DOMBridge.prototype.getChildAt = function (parent, index) {
return parent.children.item(index);
};
/**
* 获取显示索引
*
* @param {Element} parent 父容器
* @param {Element} target 子显示对象
* @return {number} target在parent中的索引
* @memberof DOMBridge
*/
DOMBridge.prototype.getChildIndex = function (parent, target) {
for (var i = 0, len = parent.children.length; i < len; i++) {
if (target === parent.children.item(i))
return i;
}
return -1;
};
/**
* 通过名称获取显示对象
*
* @param {Element} parent 父容器
* @param {string} name 对象名称
* @return {Element} 显示对象
* @memberof DOMBridge
*/
DOMBridge.prototype.getChildByName = function (parent, name) {
return parent.children.namedItem(name);
};
/**
* 获取子显示对象数量
*
* @param {Element} parent 父容器
* @return {number} 子显示对象数量
* @memberof DOMBridge
*/
DOMBridge.prototype.getChildCount = function (parent) {
return parent.children.length;
};
/**
* 加载资源
*
* @param {string[]} assets 资源数组
* @param {IMediator} mediator 资源列表
* @param {(err?:Error)=>void} handler 回调函数
* @memberof DOMBridge
*/
DOMBridge.prototype.loadAssets = function (assets, mediator, handler) {
// 开始加载皮肤列表
if (assets)
assets = assets.concat();
loadNext();
function loadNext() {
if (!assets || assets.length <= 0) {
// 调用回调
handler();
}
else {
var skin = assets.shift();
assetsManager.loadAssets(skin, function (result) {
if (result instanceof Error)
handler(result);
else
loadNext();
});
}
}
};
/**
* 监听事件,从这个方法监听的事件会在中介者销毁时被自动移除监听
*
* @param {EventTarget} target 事件目标对象
* @param {string} type 事件类型
* @param {(evt:Event)=>void} handler 事件处理函数
* @param {*} [thisArg] this指向对象
* @memberof DOMBridge
*/
DOMBridge.prototype.mapListener = function (target, type, handler, thisArg) {
var key = getObjectHashs(target, type, handler, thisArg);
// 判断是否已经存在该监听,如果存在则不再监听
if (this._listenerDict[key])
return;
// 监听
var listener = function (evt) {
// 调用回调
handler.call(thisArg || this, evt);
};
target.addEventListener(type, listener);
// 记录监听
this._listenerDict[key] = listener;
};
/**
* 注销监听事件
*
* @param {EventTarget} target 事件目标对象
* @param {string} type 事件类型
* @param {(evt:Event)=>void} handler 事件处理函数
* @param {*} [thisArg] this指向对象
* @memberof DOMBridge
*/
DOMBridge.prototype.unmapListener = function (target, type, handler, thisArg) {
var key = getObjectHashs(target, type, handler, thisArg);
// 判断是否已经存在该监听,如果存在则移除监听
var listener = this._listenerDict[key];
if (listener) {
target.removeEventListener(type, listener);
// 移除记录
delete this._listenerDict[key];
}
};
/**
* 为绑定的列表显示对象包装一个渲染器创建回调
*
* @param {Element} target BindFor指令指向的显示对象
* @param {(key?:any, value?:any, renderer?:Element)=>void} handler 渲染器创建回调
* @returns {*} 返回一个备忘录对象,会在赋值时提供
* @memberof IBridge
*/
DOMBridge.prototype.wrapBindFor = function (target, handler) {
var parent = target.parentElement;
// 生成一个from节点和一个to节点,用来占位
var from = this.createEmptyDisplay();
var to = this.createEmptyDisplay();
parent && parent.insertBefore(from, target);
parent && parent.insertBefore(to, target);
// 移除显示
parent && parent.removeChild(target);
// 返回备忘录
return { parent: parent, from: from, to: to, handler: handler };
};
/**
* 为列表显示对象赋值
*
* @param {Element} target BindFor指令指向的显示对象
* @param {*} datas 数据集合
* @param {*} memento wrapBindFor返回的备忘录对象
* @memberof IBridge
*/
DOMBridge.prototype.valuateBindFor = function (target, datas, memento) {
// 移除已有的列表项显示
var parent = memento.parent;
if (parent) {
var fromIndex = this.getChildIndex(parent, memento.from);
var toIndex = this.getChildIndex(parent, memento.to);
for (var i = fromIndex + 1; i < toIndex; i++) {
this.removeChildAt(parent, fromIndex + 1);
}
}
// 添加新的渲染器
for (var key in datas) {
var newElement = target.cloneNode(true);
// 拷贝子孙对象引用
copyRef(newElement, newElement);
// 添加显示
parent && parent.insertBefore(newElement, memento.to);
// 使用cloneNode方法复制渲染器
memento.handler(key, datas[key], newElement);
}
};
/** 提供静态类型常量 */
DOMBridge.TYPE = "DOM";
return DOMBridge;
}());
export default DOMBridge;